Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> return; } Node nodeToInsertAfter = findNodeToInsertAfter(n); nodeToInsertAfter = addMarker(funType, nodeToInsertAfter, null); TreeSet<ObjectType> stuff = Sets.newTreeSet(ALPHA); Iterables.addAll(stuff, funType.getAllImplementedInterfaces()); for (ObjectType interfaceType : stuff) { nodeToInsertAfter = addMarker(funType, nodeToInsertAfter, interfaceType); } } private Node addMarker( FunctionType funType, Node nodeToInsertAfter, @Nullable ObjectType interfaceType) { if (funType.getSource() == null) { return nodeToInsertAfter; } String className = NodeUtil.getFunctionName(funType.getSource()); // This can happen with anonymous classes declared with the type // {@code Function}. if (className == null) { return nodeToInsertAfter; } Node classNode = NodeUtil.newQualifiedNameNode(className, -1, -1); Node marker = Node.newString( interfaceType == null ? "instance_of__" + className : "implements__" + interfaceType.getReferenceName()); Node assign = new Node(Token.EXPR_RESULT, new Node(Token.ASSIGN, new Node(Token.GETELEM, new Node(Token.GETPROP, classNode, Node.newString("prototype")), marker), new Node(Token.TRUE))); nodeToInsertAfter.getParent().addChildAfter(assign, nodeToInsertAfter); compiler.reportCodeChange(); nodeToInsertAfter = assign; return nodeToInsertAfter; } /** * Find the node to insert the markers after. Typically, this node * corresponds to the constructor declaration, but we want to skip any of * the white-listed function calls. * * @param n the constructor function node * @return the node to insert after */ private Node findNodeToInsertAfter(Node n) { Node nodeToInsertAfter = findEnclosingConstructorDeclaration(n); Node next = nodeToInsertAfter.getNext(); while (next != null && isClassDefiningCall(next)) { nodeToInsertAfter = next; next = nodeToInsertAfter.getNext(); } return nodeToInsertAfter; }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> private Node findEnclosingConstructorDeclaration(Node n) { while (n.getParent().getType() != Token.SCRIPT && n.getParent().getType() != Token.BLOCK) { n = n.getParent(); } return n; } private boolean isClassDefiningCall(Node next) { return NodeUtil.isExprCall(next) && compiler.getCodingConvention().getClassesDefinedByCall( next.getFirstChild()) != null; } } /** * Insert calls to the runtime type checking function {@code checkType}, which * takes an expression to check and a list of checkers (one of which must * match). It returns the expression back to facilitate checking of return * values. We have checkers for value types, class types (user-defined and * externed), and interface types. */ private static class AddChecks extends NodeTraversal.AbstractPostOrderCallback { private final AbstractCompiler compiler; private AddChecks(AbstractCompiler compiler) { this.compiler = compiler; } checkNode = new Node(Token.EXPR_RESULT, checkNode); if (insertionPoint == null) { block.addChildToFront(checkNode); } else { block.addChildAfter(checkNode, insertionPoint); } compiler.reportCodeChange(); paramName = paramName.getNext(); insertionPoint = checkNode; } } private void visitReturn(NodeTraversal t, Node n) { Node function = t.getEnclosingFunction(); FunctionType funType = (FunctionType) function.getJSType(); Node retValue = n.getFirstChild(); if (retValue == null) { return; } Node checkNode = createCheckTypeCallNode( funType.getReturnType(), retValue.cloneTree()); if (checkNode == null) { return; } n.replaceChild(retValue, checkNode); compiler.reportCodeChange(); } /** * Creates a function call to check that the given expression matches the * given type at runtime. * * <p>For example, if the type is {@code (string|Foo)}, the function call is * {@code checkType(expr, [valueChecker('string'), classChecker('Foo')])}. * * @return the function

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> addVar(String name) { int vIndex = itsVariableNames.get(name, -1); if (vIndex != -1) { // There's already a variable or parameter with this name. if (vIndex >= varStart) { Object v = itsConst.get(vIndex); if (v != null) return DUPLICATE_CONST; else return DUPLICATE_VAR; } else return DUPLICATE_PARAMETER; } int index = itsVariables.size(); itsVariables.add(name); itsConst.add(null); itsVariableNames.put(name, index); return NO_DUPLICATE; } public final boolean addConst(String name) { int vIndex = itsVariableNames.get(name, -1); if (vIndex != -1) { // There's already a variable or parameter with this name. return false; } int index = itsVariables.size(); itsVariables.add(name); itsConst.add(name); itsVariableNames.put(name, index); return true; } public final void removeParamOrVar(String name) { int i = itsVariableNames.get(name, -1); if (i != -1) { itsVariables.remove(i); itsVariableNames.remove(name); ObjToIntMap.Iterator iter = itsVariableNames.newIterator(); for (iter.start(); !iter.done(); iter.next()) { int v = iter.getValue(); if (v > i) { iter.setValue(v - 1); } } } } public final Object getCompilerData() { return compilerData; } public final void setCompilerData(Object data) { if (data == null) throw new IllegalArgumentException(); // Can only call once if (compilerData != null) throw new IllegalStateException(); compilerData = data; } private int encodedSourceStart; private int encodedSourceEnd; private String sourceName; private int baseLineno = -1; private int endLineno = -1; private ObjArray functions; private ObjArray regexps; // a list of the formal parameters and local variables private ObjArray itsVariables

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>; } @Override public List<GraphvizNode> getGraphvizNodes() { List<GraphvizNode> nodeList = Lists.newArrayListWithCapacity(nodes.size()); for (LinkedDirectedGraphNode<N, E> node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public String getName() { return "LinkedGraph"; } @Override public boolean isDirected() { return true; } @Override public Collection<GraphNode<N, E>> getNodes() { return Collections.<GraphNode<N, E>>unmodifiableCollection(nodes.values()); } @Override public List<GraphNode<N, E>> getNeighborNodes(N value) { DiGraphNode<N, E> node = getDirectedGraphNode(value); return getNeighborNodes(node); } public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) { List<GraphNode<N, E>> result = Lists.newArrayList(); for (Iterator<GraphNode<N, E>> i = ((LinkedDirectedGraphNode<N, E>) node).neighborIterator();i.hasNext();) { result.add(i.next()); } return result; } @Override public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) { LinkedDirectedGraphNode<N, E> node = nodes.get(value); Preconditions.checkNotNull(node); return node.neighborIterator(); } @Override public List<GraphEdge<N, E>> getEdges() { List<GraphEdge<N, E>> result = Lists.newArrayList(); for (DiGraphNode<N, E> node : nodes.values()) { for (DiGraphEdge<N, E> edge : node.getOutEdges()) { result.add(edge); } } return Collections.unmodifiableList(result); } @Override public int getNodeDegree(N value) { DiGraphNode<N, E> node = getNodeOrFail(value); return node.getInEdges().size() + node.getOutEdges().size(); } /** * A directed graph node that stores outgoing

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> edges and incoming edges as an * list within the node itself. */ static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>, GraphvizNode { List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList(); List<DiGraphEdge<N, E>> outEdgeList = Lists.newArrayList(); protected final N value; protected Annotation annotation; protected int id; private static int totalNodes = 0; /** * Constructor * * @param nodeValue Node's value. */ LinkedDirectedGraphNode(N nodeValue) { this.value = nodeValue; this.id = totalNodes++; } @Override public N getValue() { return value; } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + id; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String toString() { return getLabel(); } @Override public List<DiGraphEdge<N, E>> getInEdges() { return inEdgeList; } @Override public List<DiGraphEdge<N, E>> getOutEdges() { return outEdgeList; } private Iterator<GraphNode<N, E>> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator<GraphNode<N, E>> { private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator(); private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator(); @Override public boolean hasNext() { return in.hasNext() || out.hasNext(); } @Override public GraphNode<N, E> next() { boolean isOut = !in.hasNext(); Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>; DiGraphEdge<N, E> s = curIterator.next(); return isOut ? s.getDestination() : s.getSource(); } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class LinkedDirectedGraphEdge<N, E> implements DiGraphEdge<N, E>, GraphvizEdge { private DiGraphNode<N, E> sourceNode; private DiGraphNode<N, E> destNode; protected final E value; protected Annotation annotation; /** * Constructor. * * @param edgeValue Edge Value. */ LinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode, E edgeValue, DiGraphNode<N, E> destNode) { this.value = edgeValue; this.sourceNode = sourceNode; this.destNode = destNode; } @Override public DiGraphNode<N, E> getSource() { return sourceNode; } @Override public DiGraphNode<N, E> getDestination() { return destNode; } @Override public void setDestination(DiGraphNode<N, E> node) { destNode = node; } @Override public void setSource(DiGraphNode<N, E> node) { sourceNode = node; } @Override public E getValue() { return value; } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } @Override public String getColor() { return "black"; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String getNode1Id() { return ((LinkedDirectedGraphNode<N, E>) sourceNode).getId(); } @Override public String getNode2Id() { return ((LinkedDirectedGraphNode<N, E>) destNode).getId(); } @Override public String toString()

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> extends NodeTraversal.AbstractPostOrderCallback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } /** * * In the AST that Rhino gives us, it needs to make a distinction * between jsdoc on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ @SuppressWarnings("fallthrough") public void visit(NodeTraversal t, Node n, Node parent) { int nType = n.getType(); switch (nType) { case Token.STRING: // There are only two cases where a string token // may be a variable reference: The right side of a GETPROP // or an OBJECTLIT key. if (parent.getType() != Token.OBJECTLIT && parent.getType() != Token.GETPROP) { break; } // fall-through case Token.NAME: String nString = n.getString(); if (nType == Token.NAME && n.getParent().getType() == Token.CALL && "eval".equals(nString)) { n.putBooleanProp(Node.DIRECT_EVAL, true); } if (NodeUtil.isConstantByConvention(convention, n, parent)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } break; case Token.FUNCTION: JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null) { // Look for the info on other nodes. if (parent.getType() == Token.ASSIGN) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.getType() == Token.NAME) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } // Compute which function parameters are

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } break; case Token.OBJECTLIT: if (n.getType() == Token.OBJECTLIT) { for (Node key = n.getFirstChild(); key != null; key = key.getNext().getNext()) { Node value = key.getNext(); if (key.getJSDocInfo() != null && key.getNext().getType() == Token.FUNCTION) { value.setJSDocInfo(key.getJSDocInfo()); } } } break; } // TODO(johnlenz): Determine if it is possible to simply use the javadoc // everywhere rather than use IS_DISPATCHER. /* * Translate dispatcher info into the property expected node. */ if (n.getJSDocInfo() != null && n.getJSDocInfo().isJavaDispatch()) { if (n.getType() == Token.ASSIGN) { Node fnNode = n.getLastChild(); Preconditions.checkState(fnNode.getType() == Token.FUNCTION); fnNode.putBooleanProp(Node.IS_DISPATCHER, true); } } } } }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>instance, implemented); } } } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required); } private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSTypeNative required) { mismatch(t, n, msg, found, getNativeType(required)); } private void mismatch(String sourceName, Node n, String msg, JSType found, JSType required) { registerMismatch(found, required); if (shouldReport) { compiler.report( JSError.make(sourceName, n, TYPE_MISMATCH_WARNING, formatFoundRequired(msg, found, required))); } } private void registerMismatch(JSType found, JSType required) { // Don't register a mismatch for differences in null or undefined or if the // code didn't downcast. found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (found.canAssignTo(required) || required.canAssignTo(found)) { return; } mismatches.add(new TypeMismatch(found, required)); if (found instanceof FunctionType && required instanceof FunctionType) { FunctionType fnTypeA = ((FunctionType) found); FunctionType fnTypeB = ((FunctionType) required); Iterator<Node> paramItA = fnTypeA.getParameters().iterator(); Iterator<Node> paramItB = fnTypeB.getParameters().iterator(); while (paramItA.hasNext() && paramItB.hasNext()) { registerIfMismatch(paramItA.next().getJSType(), paramItB.next().getJSType()); } registerIfMismatch(fnTypeA.getReturnType(), fnTypeB.getReturnType()); } } private void registerIfMismatch(JSType found, JSType required) { if (found != null && required != null && !found.canAssignTo(required)) { registerMismatch(found, required); } } /** * Formats a found/required error message. */ private String formatFoundRequired(String description, JSType

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> not belong to this scope, but are assigned * in this scope. */ private final Multimap<Scope, Var> assignedOuterLocalVars = HashMultimap.create(); /** * Vars that we should not map out type flow for. */ private final Set<String> unflowableVarNames = Sets.newHashSet(); TypeInference(AbstractCompiler compiler, ControlFlowGraph<Node> cfg, ReverseAbstractInterpreter reverseInterpreter, Scope functionScope, Map<String, AssertionFunctionSpec> assertionFunctionsMap) { this(compiler, cfg, reverseInterpreter, functionScope, assertionFunctionsMap, ImmutableSet.<Var>of()); } /** * @param unflowableVars Do not do infer flow on the types of these vars. * @param assertionFunctionsMap */ // TODO(nicksantos): Create a builder for this class. TypeInference(AbstractCompiler compiler, ControlFlowGraph<Node> cfg, ReverseAbstractInterpreter reverseInterpreter, Scope functionScope, Map<String, AssertionFunctionSpec> assertionFunctionsMap, Collection<Var> unflowableVars) { super(cfg, new LinkedFlowScope.FlowScopeJoinOp()); this.compiler = compiler; this.registry = compiler.getTypeRegistry(); this.reverseInterpreter = reverseInterpreter; this.syntacticScope = functionScope; this.functionScope = LinkedFlowScope.createEntryLattice(functionScope); this.assertionFunctionsMap = assertionFunctionsMap; for (Var unflowableVar : unflowableVars) { String name = unflowableVar.getName(); if (functionScope.getVar(name) == unflowableVar) { this.unflowableVarNames.add(name); } } Iterator<Var> varIt = functionScope.getVars(); while (varIt.hasNext()) { Var var = varIt.next(); if (this.unflowableVarNames.contains(var.getName())) { continue; } // For each local variable declared with the VAR keyword, the entry // type is VOID. if (var.getParentNode() != null && var.getType() == null && // no declared type var.getParentNode().getType() == Token.VAR && !var.isExtern()) { this.functionScope.infer

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>FlowScope = traverse(condition, output.createChildFlowScope()); } newScope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( condition, conditionFlowScope, branch == Branch.ON_TRUE); } } break; } result.add(newScope.optimize()); } return result; } private FlowScope traverse(Node n, FlowScope scope) { switch (n.getType()) { case Token.ASSIGN: scope = traverseAssign(n, scope); break; case Token.NAME: scope = traverseName(n, scope); break; case Token.GETPROP: scope = traverseGetProp(n, scope); break; case Token.AND: scope = traverseAnd(n, scope).getJoinedFlowScope() .createChildFlowScope(); break; case Token.OR: scope = traverseOr(n, scope).getJoinedFlowScope() .createChildFlowScope(); break; case Token.HOOK: scope = traverseHook(n, scope); break; case Token.OBJECTLIT: scope = traverseObjectLiteral(n, scope); break; case Token.CALL: scope = traverseCall(n, scope); break; case Token.NEW: scope = traverseNew(n, scope); break; case Token.ASSIGN_ADD: case Token.ADD: scope = traverseAdd(n, scope); break; case Token.POS: case Token.NEG: scope = traverse(n.getFirstChild(), scope); // Find types. n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.NULL: n.setJSType(getNativeType(NULL_TYPE)); break; case Token.VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.ARRAYLIT: scope = traverseArrayLiteral(n, scope); break; case Token.REF_SPECIAL: n.setJSType(getNativeType(UNKNOWN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.THIS: n.setJSType(scope.getType

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block traverseBranch(n.getFirstChild(), n); traverseBranch(n.getFirstChild().getNext().getNext(), n); break; case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getType() == Token.FUNCTION); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.getType() == Token.BLOCK); traverseBranch(body, n); popScope(); } /** Examines the functions stack for

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator<Node> it = scopeRoots.descendingIterator(); while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph<Node> getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> } if (n.getType() == Token.FUNCTION) { moveNamedFunctions(n.getLastChild()); } } // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are // fixed. /** * Limit the number of special cases where LABELs need to be handled. Only * BLOCK and loops are allowed to be labeled. Loop labels must remain in * place as the named continues are not allowed for labeled blocks. */ private void normalizeLabels(Node n) { Preconditions.checkArgument(n.getType() == Token.LABEL); Node last = n.getLastChild(); switch (last.getType()) { case Token.LABEL: case Token.BLOCK: case Token.FOR: case Token.WHILE: case Token.DO: return; default: Node block = new Node(Token.BLOCK); block.copyInformationFrom(last); n.replaceChild(last, block); block.addChildToFront(last); reportCodeChange("LABEL normalization"); return; } } /** * Bring the initializers out of FOR loops. These need to be placed * before any associated LABEL nodes. This needs to be done from the top * level label first so this is called as a pre-order callback (from * shouldTraverse). * * @param n The node to inspect. * @param before The node to insert the initializer before. * @param beforeParent The parent of the node before which the initializer * will be inserted. */ private void extractForInitializer( Node n, Node before, Node beforeParent) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); Node insertBefore = (before == null) ? c : before; Node insertBeforeParent = (before == null) ? n : beforeParent; switch (c.getType()) { case Token.LABEL: extractForInitializer(c, insertBefore, insertBeforeParent); break; case Token.FOR: if (!NodeUtil.isForIn(c) && c.getFirstChild().getType() != Token.EMPTY) { Node init = c.getFirstChild(); Node empty = new Node(Token.EMPTY);

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> empty.copyInformationFrom(c); c.replaceChild(init, empty); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.getType() == Token.VAR) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect. */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.getType() == Token.VAR) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = new Node( Token.VAR, name, n.getLineno(), n.getCharno()); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) { Preconditions.checkState( functionBody.getParent().getType() == Token.FUNCTION); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any declarations at the beginning of the function body, they // are already in the right place. while (current != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Readd the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler implements SyntacticScopeCreator.RedeclarationHandler { /** * Remove duplicate VAR declarations encountered discovered during * scope creation. */ @Override public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { Preconditions.checkState(n.getType() == Token.NAME); Var v = s.getVar(name); // If name is "arguments", Var maybe null. if (v != null && v.getParentNode().getType() == Token.CATCH) { // Redeclaration of a catch expression variable is hard to model // without support for "with" expressions. // The EcmaScript spec (section 12.14), declares that a catch // "catch (e) {}" is handled like "with ({'e

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> = it.next(); if (var.isTypeInferred()) { JSType type = var.getType(); if (type == null || type.isUnknownType()) { JSType flowType = getSlot(var.getName()).getType(); var.setType(flowType); } } } } /** * Remove flow scopes that add nothing to the flow. */ // NOTE(nicksantos): This function breaks findUniqueRefinedSlot, because // findUniqueRefinedSlot assumes that this scope is a direct descendant // of blindScope. This is not necessarily true if this scope has been // optimize()d and blindScope has not. This should be fixed. For now, // we only use optimize() where we know that we won't have to do // a findUniqueRefinedSlot on it. @Override public LinkedFlowScope optimize() { LinkedFlowScope current; for (current = this; current.parent != null && current.lastSlot == current.parent.lastSlot; current = current.parent) {} return current; } /** Join the two FlowScopes. */ static class FlowScopeJoinOp extends JoinOp.BinaryJoinOp<FlowScope> { @SuppressWarnings("unchecked") @Override public FlowScope apply(FlowScope a, FlowScope b) { // To join the two scopes, we have to LinkedFlowScope linkedA = (LinkedFlowScope) a; LinkedFlowScope linkedB = (LinkedFlowScope) b; linkedA.frozen = true; linkedB.frozen = true; if (linkedA.optimize() == linkedB.optimize()) { return linkedA.createChildFlowScope(); } return new LinkedFlowScope(new FlatFlowScopeCache(linkedA, linkedB)); } } @Override public boolean equals(Object other) { if (other instanceof LinkedFlowScope) { LinkedFlowScope that = (LinkedFlowScope) other; if (this.optimize() == that.optimize()) { return true; } // If two flow scopes are in the same function, then they could have // two possible function scopes: the real one and the BOTTOM scope. // If they have different function scopes, we *should* iterate thru all // the variables in each scope and

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>.size() - 1; i >= 0; i--) { JSModule m = modulesAtDepth.get(i); if (dependsOn(m1, m) && dependsOn(m2, m)) { return m; } } } return null; } /** * Finds the deepest common dependency of two modules, including the * modules themselves. * * @param m1 A module in this graph * @param m2 A module in this graph * @return The deepest common dep of {@code m1} and {@code m2}, or null if * they have no common dependencies */ public JSModule getDeepestCommonDependencyInclusive( JSModule m1, JSModule m2) { if (m2 == m1 || dependsOn(m2, m1)) { return m1; } else if (dependsOn(m1, m2)) { return m2; } return getDeepestCommonDependency(m1, m2); } /** Returns the deepest common dependency of the given modules. */ public JSModule getDeepestCommonDependencyInclusive( Collection<JSModule> modules) { Iterator<JSModule> iter = modules.iterator(); JSModule dep = iter.next(); while (iter.hasNext()) { dep = getDeepestCommonDependencyInclusive(dep, iter.next()); } return dep; } /** * Creates an iterable over the transitive dependencies of module {@code m} * in a non-increasing depth ordering. The result does not include the module * {@code m}. * * @param m A module in this graph * @return The transitive dependencies of module {@code m} */ Set<JSModule> getTransitiveDepsDeepestFirst(JSModule m) { Set<JSModule> deps = dependencyMap.get(m); if (deps != null) { return deps; } deps = new TreeSet<JSModule>(new InverseDepthComparator()); addDeps(deps, m); dependencyMap.put(m, deps); return deps; } /** * Adds a module's transitive dependencies to a set. */ private void addDeps(Set<JSModule

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>> deps, JSModule m) { for (JSModule dep : m.getDependencies()) { deps.add(dep); addDeps(deps, dep); } } /** * Replaces any files that are found multiple times with a single instance in * the closest parent module that is common to all modules where it appears. * * JSCompiler normally errors if you attempt to compile modules containing the * same file. This method can be used to remove duplicates before compiling * to avoid such an error. */ public void coalesceDuplicateFiles() { Multimap<String, JSModule> fileRefs = LinkedHashMultimap.create(); for (JSModule module : moduleDepths.keySet()) { for (CompilerInput jsFile : module.getInputs()) { fileRefs.put(jsFile.getName(), module); } } for (String path : fileRefs.keySet()) { Collection<JSModule> refModules = fileRefs.get(path); if (refModules.size() > 1) { JSModule depModule = getDeepestCommonDependencyInclusive(refModules); CompilerInput file = refModules.iterator().next().getByName(path); for (JSModule module : refModules) { if (module != depModule) { module.removeByName(path); } } if (!refModules.contains(depModule)) { depModule.add(file); } } } } /** * Sort the sources of modules in dependency-order. * * If a source file provides a symbol that is not required, then that * file will be removed from the compilation. If a source file provides * a symbol that is not required until a later module, then that * file will be moved to the later module. * * @param inputs The original list of sources. Used to ensure that the sort * is stable. * @return The sorted list of sources. */ List<CompilerInput> manageDependencies(List<CompilerInput> inputs) throws CircularDependencyException { SortedDependencies<CompilerInput> sorter = new SortedDependencies<CompilerInput>(inputs); List<CompilerInput> inputsWithoutProvides = sorter.getInputsWithoutProvides(); // The order of inputs, sorted independently of modules. List<

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> /** * Gets the names of the modules that this module depends on, * sorted alphabetically. */ List<String> getSortedDependencyNames() { List<String> names = Lists.newArrayList(); for (JSModule module : getDependencies()) { names.add(module.getName()); } Collections.sort(names); return names; } /** * Returns the transitive closure of dependencies starting from the * dependencies of this module. */ public Set<JSModule> getAllDependencies() { Set<JSModule> allDeps = Sets.newHashSet(deps); List<JSModule> workList = Lists.newArrayList(deps); while (workList.size() > 0) { JSModule module = workList.remove(workList.size() - 1); for (JSModule dep : module.getDependencies()) { if (allDeps.add(dep)) { workList.add(dep); } } } return allDeps; } /** Returns this module and all of its dependencies in one list. */ public Set<JSModule> getThisAndAllDependencies() { Set<JSModule> deps = getAllDependencies(); deps.add(this); return deps; } /** * Gets this module's list of source code inputs. * * @return A list that may be empty but not null */ public List<CompilerInput> getInputs() { return inputs; } /** Returns the input with the given name or null if none. */ public CompilerInput getByName(String name) { for (CompilerInput input : inputs) { if (name.equals(input.getName())) { return input; } } return null; } /** * Removes any input with the given name. Returns whether any were removed. */ public boolean removeByName(String name) { boolean found = false; Iterator<CompilerInput> iter = inputs.iterator(); while (iter.hasNext()) { CompilerInput file = iter.next(); if (name.equals(file.getName())) { iter.remove(); file.setModule(null); found = true; } } return found; } /** Returns the module name (primarily for debugging). */ @Override

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.REF_SPECIAL: ensureTyped(t, n); break; case Token.GET_REF: ensureTyped(t, n, getJSType(n.getFirstChild())); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, NUMBER_TYPE); } else { typeable = false; } break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.STRING: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, STRING_TYPE); } else { typeable = false; } break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.getType() == Token.ASSIGN && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !NodeUtil.isExpressionNode(parent); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left,

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.getType() == Token.OBJECTLIT) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } checkNoTypeCheckSection(n, false); } /** * Counts the given node in the typed statistics. * @param n a node that should be typed */ private void doPercentTypedAccounting(NodeTraversal t, Node n) { JSType type = n.getJSType(); if (type == null) { nullCount++; } else if (type.isUnknownType()) { if (reportUnknownTypes.isOn()) { compiler.report( t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } unknownCount++; } else { typedCount++; } } /** * Visits an assignment <code>lvalue = rvalue</code>. If the * <code>lvalue</code> is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.getType() == Token.ASSIGN</code>

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> n) { Node child = n.getFirstChild(); JSType childType = getJSType(child).restrictByNotNullOrUndefined(); if (!childType.canBeCalled()) { report(t, n, NOT_CALLABLE, childType.toString()); ensureTyped(t, n); return; } // A couple of types can be called as if they were functions. // If it is a function type, then validate parameters. if (childType instanceof FunctionType) { FunctionType functionType = (FunctionType) childType; // Non-native constructors should never be called directly. if (functionType.isConstructor() && !functionType.isNativeObjectType()) { report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); } visitParameterList(t, n, functionType); ensureTyped(t, n, functionType.getReturnType()); } else { ensureTyped(t, n); } // TODO: Add something to check for calls of RegExp objects, which is not // supported by IE. Either say something about the return type or warn // about the non-portability of the call or both. } /** * Visits the parameters of a CALL or a NEW node. */ private void visitParameterList(NodeTraversal t, Node call, FunctionType functionType) { Iterator<Node> arguments = call.children().iterator(); arguments.next(); // skip the function name Iterator<Node> parameters = functionType.getParameters().iterator(); int ordinal = 0; while (arguments.hasNext() && parameters.hasNext()) { Node parameter = parameters.next(); Node argument = arguments.next(); ordinal++; validator.expectArgumentMatchesParameter(t, argument, getJSType(argument), getJSType(parameter), call, ordinal); } int numArgs = call.getChildCount() - 1; int minArgs = functionType.getMinArguments(); int maxArgs = functionType.getMaxArguments(); if (minArgs > numArgs || maxArgs < numArgs) { report(t, call, WRONG_ARGUMENT_COUNT, validator.getReadableJSTypeName(call.getFirstChild(), false), String.valueOf(numArgs), String.valueOf(minArgs), maxArgs

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> value used for initialization of the enum * @param primitiveType The type of each element of the enum. */ private void checkEnumInitializer( NodeTraversal t, Node value, JSType primitiveType) { if (value.getType() == Token.OBJECTLIT) { // re-using value as the value of the object literal and advancing twice value = value.getFirstChild(); value = (value == null) ? null : value.getNext(); while (value != null) { // the value's type must be assignable to the enum's primitive type validator.expectCanAssignTo(t, value, getJSType(value), primitiveType, "element type must match enum's type"); // advancing twice value = value.getNext(); value = (value == null) ? null : value.getNext(); } } else if (value.getJSType() instanceof EnumType) { // TODO(user): Remove the instanceof check in favor // of a type.isEnumType() predicate. Currently, not all enum types are // implemented by the EnumClass, e.g. the unknown type and the any // type. The types need to be defined by interfaces such that an // implementation can implement multiple types interface. EnumType valueEnumType = (EnumType) value.getJSType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, primitiveType, "incompatible enum element types"); } else { // The error condition is handled in TypedScopeCreator. } } /** * This predicate is used to determine if the node represents an expression * that is a Reference according to JavaScript definitions. * * @param n The node being checked. * @return true if the sub-tree n is a reference, false otherwise. */ private static boolean isReference(Node n) { switch (n.getType()) { case Token.GETELEM: case Token.GETPROP: case Token.NAME: return true; default: return false; } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>Info(fileOverviewInfo); jsdocParser.parse(); return jsdocParser; } /** Attach JSDocInfo to a node, if we can find one. */ private void attachJsDoc(Comment comment, JSDocInfo info) { Collection<NodeWithJsDoc> candidates = nodesWithJsDoc.get(comment.getValue()); if (candidates.isEmpty()) { return; } Iterator<NodeWithJsDoc> candidateIter = candidates.iterator(); Node node = candidateIter.next().node; candidateIter.remove(); node.setJSDocInfo(info); } private int position2charno(int position) { int lineIndex = sourceString.lastIndexOf('\n', position); if (lineIndex == -1) { return position; } else { // Subtract one for initial position being 0. return position - lineIndex - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = newNode(transformTokenType(n.getType())); for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) { node.addChildToBack(transform((AstNode)child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret = transform(n); if (ret.getType() == Token.STRING) { ret.putBooleanProp(Node.QUOTED_PROP, true); } else if (ret.getType() == Token.NAME) { ret.setType(Token

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>NumberLiteral(NumberLiteral literalNode) { return newNumberNode(literalNode.getNumber()); } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (el.isGetter()) { reportGetter(el); } else if (el.isSetter()) { reportSetter(el); } else { node.addChildToBack(transformAsString(el.getLeft())); node.addChildToBack(transform(el.getRight())); } } return node; } @Override Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { return newNode( Token.GETPROP, transform(getNode.getTarget()), transformAsString(getNode.getProperty())); } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = newNode(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>.javascript.jscomp.mozilla.rhino.Token.RETHROW: return Token.RETHROW; case com.google.javascript.jscomp.mozilla.rhino.Token.IN: return Token.IN; case com.google.javascript.jscomp.mozilla.rhino.Token.INSTANCEOF: return Token.INSTANCEOF; case com.google.javascript.jscomp.mozilla.rhino.Token.LOCAL_LOAD: return Token.LOCAL_LOAD; case com.google.javascript.jscomp.mozilla.rhino.Token.GETVAR: return Token.GETVAR; case com.google.javascript.jscomp.mozilla.rhino.Token.SETVAR: return Token.SETVAR; case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH_SCOPE: return Token.CATCH_SCOPE; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_INIT_KEYS: return Token.ENUM_INIT_KEYS; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_INIT_VALUES: return Token.ENUM_INIT_VALUES; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_NEXT: return Token.ENUM_NEXT; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_ID: return Token.ENUM_ID; case com.google.javascript.jscomp.mozilla.rhino.Token.THISFN: return Token.THISFN; case com.google.javascript.jscomp.mozilla.rhino.Token.RETURN_RESULT: return Token.RETURN_RESULT; case com.google.javascript.jscomp.mozilla.rhino.Token.ARRAYLIT: return Token.ARRAYLIT; case com.google.javascript.jscomp.mozilla.rhino.Token.OBJECTLIT: return Token.OBJECTLIT; case com.google.javascript.jscomp.mozilla.rhino.Token.GET_REF: return Token.GET_REF; case com.google.javascript.jscomp.mozilla.rhino.Token.SET_REF: return Token.SET_REF; case com.google.javascript.jscomp.mozilla.rh

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> Sets.<INPUT>newHashSet()); } private List<INPUT> findCycle( INPUT current, Set<INPUT> subGraph, Multimap<INPUT, INPUT> deps, Set<INPUT> covered) { if (covered.add(current)) { List<INPUT> cycle = findCycle( findRequireInSubGraphOrFail(current, subGraph), subGraph, deps, covered); // Don't add the input to the list if the cycle has closed already. if (cycle.get(0) != cycle.get(cycle.size() - 1)) { cycle.add(current); } return cycle; } else { // Explicitly use the add() method, to prevent a generics constructor // warning that is dumb. The condition it's protecting is // obscure, and I think people have proposed that it be removed. List<INPUT> cycle = Lists.<INPUT>newArrayList(); cycle.add(current); return cycle; } } private INPUT findRequireInSubGraphOrFail(INPUT input, Set<INPUT> subGraph) { for (String symbol : input.getRequires()) { INPUT candidate = provideMap.get(symbol); if (subGraph.contains(candidate)) { return candidate; } } throw new IllegalStateException("no require found in subgraph"); } /** * @param cycle A cycle in reverse-dependency order. */ private String cycleToString(List<INPUT> cycle) { List<String> symbols = Lists.newArrayList(); for (int i = cycle.size() - 1; i >= 0; i--) { symbols.add(cycle.get(i).getProvides().iterator().next()); } symbols.add(symbols.get(0)); return Joiner.on(" -> ").join(symbols); } public List<INPUT> getSortedList() { return Collections.<INPUT>unmodifiableList(sortedList); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. */ public List<INPUT> getSortedDependenciesOf(List<INPUT>

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } public Scope createScope(Node n, Scope parent) { sourceName = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); sourceName = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>(); declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext().getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name);

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>.<L>newArrayList(latticeA, latticeB)); } /** * Checks whether the analysis is a forward flow analysis or backward flow * analysis. * * @return {@code true} if it is a forward analysis. */ abstract boolean isForward(); /** * Computes the output state for a given node and input state. * * @param node The node. * @param input Input lattice that should be read-only. * @return Output lattice. */ abstract L flowThrough(N node, L input); /** * Finds a fixed-point solution using at most {@link #MAX_STEPS} * iterations. * * @see #analyze(int) */ final void analyze() { analyze(MAX_STEPS); } /** * Finds a fixed-point solution. The function has the side effect of replacing * the existing node annotations with the computed solutions using {@link * com.google.javascript.jscomp.graph.GraphNode#setAnnotation(Annotation)}. * * <p>Initially, each node's input and output flow state contains the value * given by {@link #createInitialEstimateLattice()} (with the exception of the * entry node of the graph which takes on the {@link #createEntryLattice()} * value. Each node will use the output state of its predecessor and compute a * output state according to the instruction. At that time, any nodes that * depends on the node's newly modified output value will need to recompute * their output state again. Each step will perform a computation at one node * until no extra computation will modify any existing output state anymore. * * @param maxSteps Max number of iterations before the method stops and throw * a {@link MaxIterationsExceededException}. This will prevent the * analysis from going into a infinite loop. */ final void analyze(int maxSteps) { initialize(); int step = 0; while (!orderedWorkSet.isEmpty()) { if (step > maxSteps) { throw new MaxIterationsExceededException( "Analysis did not terminate after " + maxSteps + " iterations"); } DiGraphNode<N, Branch> curNode = orderedWorkSet.iterator().next(); orderedWorkSet.remove(curNode); joinInputs(cur

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> * variable local not to our definition of scope.</li> * <li>Exported variables as they can be needed after the script terminates. * </li> * <li>Names of named functions because in javascript, <i>function foo(){}</i> * does not kill <i>foo</i> in the dataflow.</li> */ static void computeEscaped(final Scope jsScope, final Set<Var> escaped, AbstractCompiler compiler) { // TODO(user): Very good place to store this information somewhere. AbstractPostOrderCallback finder = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (jsScope == t.getScope() || !NodeUtil.isName(n) || NodeUtil.isFunction(parent)) { return; } String name = n.getString(); Var var = t.getScope().getVar(name); if (var != null && var.scope == jsScope) { escaped.add(jsScope.getVar(name)); } } }; NodeTraversal t = new NodeTraversal(compiler, finder); t.traverseAtScope(jsScope); // 1: Remove the exception name in CATCH which technically isn't local to // begin with. for (Iterator<Var> i = jsScope.getVars(); i.hasNext();) { Var var = i.next(); if (var.getParentNode().getType() == Token.CATCH || compiler.getCodingConvention().isExported(var.getName())) { escaped.add(var); } } } }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> { this(new ContextualRenamer()); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used the starting context can not // be a function. Preconditions.checkState( declarationRoot.getType() != Token.FUNCTION || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (declarationRoot.getType() == Token.FUNCTION) { // Add the function parameters Node fnParams = declarationRoot.getFirstChild().getNext(); for (Node c = fnParams.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = declarationRoot.getLastChild(); findDeclaredNames(functionBody, null, renamer); } else { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null && !NodeUtil.isFunctionDeclaration(n

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>Util.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); renamer.addDeclaredName(nameNode.getString()); } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { findDeclaredNames(c, n, renamer); } } } /** * Declared names renaming policy interface. */ interface Renamer { /** * Called when a declared name is found in the local current scope. */ void addDeclaredName(String name); /** * @return A replacement name, null if oldName is unknown or should not * be replaced. */ String getReplacementName(String oldName); /** * @return Whether the constant-ness of a name should be removed. */ boolean stripConstIfReplaced(); /** * @return A Renamer for a scope within the scope of the current Renamer. */ Renamer forChildScope(); } /** * Inverts the transformation by {@link ContextualRenamer}, when possible. */ static class ContextualRenameInverter implements ScopedCallback, CompilerPass { private final AbstractCompiler compiler; // The set of names referenced in the current scope. private Set<String> referencedNames = ImmutableSet.of(); // Stack reference sets. private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>(); // Name are globally unique initially, so we don't need a per-scope map. private Map<String, List<Node>> nameMap = Maps.newHashMap(); private ContextualRenameInverter(AbstractCompiler compiler) { this.compiler = compiler; } public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, js, this); } public static String getOrginalName(String name) { int index = indexOfSeparator(name); return (index == -1) ? name : name.substring(0, index); } private static int indexOfSeparator(String name) { return name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR); } private boolean containsSeparator(String name) { return name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1; } /** *

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> Prepare a set for the new scope. */ public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } referenceStack.push(referencedNames); referencedNames = Sets.newHashSet(); } /** * Rename vars for the current scope, and merge any referenced * names into the parent scope reference set. */ public void exitScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); handleScopeVar(v); } // Merge any names that were referenced but not declared in the current // scope. Set<String> current = referencedNames; referencedNames = referenceStack.pop(); // If there isn't anything left in the stack we will be going into the // global scope: don't try to build a set of referenced names for the // global scope. if (!referenceStack.isEmpty()) { referencedNames.addAll(current); } } /** * For the Var declared in the current scope determine if it is possible * to revert the name to its orginal form without conflicting with other * values. */ void handleScopeVar(Var v) { String name = v.getName(); if (containsSeparator(name) && !getOrginalName(name).isEmpty()) { String newName = findReplacementName(name); referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List<Node> references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.getType() == Token.NAME); n.setString(newName); } compiler.reportCodeChange(); nameMap.remove(name); } } /** * Find a name usable in the local scope. */ private String findReplacementName(String name) { String original = getOrginalName(name); String newName = original; int i = 0; while (!

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> } else if (NodeUtil.isFunctionDeclaration(n) || parent.getType() == Token.NAME) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isConstructor()) { return ((FunctionType) type).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((FunctionPrototypeType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDe

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> parser = new JsDocInfoParser( new JsDocTokenStream(typeString), "typeparsing", config, NullErrorReporter.forNewRhino()); return parser.parseTopLevelTypeExpression(parser.next()); } /** * Parses a {@link JSDocInfo} object. This parsing method reads all tokens * returned by the {@link JsDocTokenStream#getJsDocToken()} method until the * {@link JsDocToken#EOC} is returned. * * @return {@code true} if JSDoc information was correctly parsed, * {@code false} otherwise */ boolean parse() { int lineno; int charno; // JSTypes are represented as Rhino AST nodes, and then resolved later. JSTypeExpression type; state = State.SEARCHING_ANNOTATION; JsDocToken token = next(); ExtractionInfo blockInfo = extractBlockComment(token); token = blockInfo.token; // If we have a block level comment, record it. if (blockInfo.string.length() > 0) { jsdocBuilder.recordBlockDescription(blockInfo.string); } // Parse the actual JsDoc. retry: for (;;) { switch (token) { case ANNOTATION: if (state == State.SEARCHING_ANNOTATION) { state = State.SEARCHING_NEWLINE; lineno = stream.getLineno(); charno = stream.getCharno(); String annotationName = stream.getString(); Annotation annotation = annotationNames.get(annotationName); if (annotation == null) { parser.addWarning("msg.bad.jsdoc.tag", annotationName, stream.getLineno(), stream.getCharno()); } else { // Mark the beginning of the annotation. jsdocBuilder.markAnnotation(annotationName, lineno, charno); switch (annotation) { case AUTHOR: ExtractionInfo authorInfo = extractSingleLineBlock(); String author = authorInfo.string; if (author.length() == 0) { parser.addWarning("msg.jsdoc.authormissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addAuthor(author); } token

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> case FILE_OVERVIEW: ExtractionInfo fileOverviewInfo = extractMultilineTextualBlock(token, WhitespaceOption.TRIM); String fileOverview = fileOverviewInfo.string; if (!jsdocBuilder.recordFileOverview(fileOverview) || fileOverviewJSDocInfo != null) { parser.addWarning("msg.jsdoc.fileoverview.extra", stream.getLineno(), stream.getCharno()); } token = fileOverviewInfo.token; continue retry; case LICENSE: case PRESERVE: ExtractionInfo preserveInfo = extractMultilineTextualBlock(token, WhitespaceOption.PRESERVE); String preserve = preserveInfo.string; if (preserve.length() > 0) { if (fileLevelJsDocBuilder != null) { fileLevelJsDocBuilder.append(preserve); } } token = preserveInfo.token; continue retry; case ENUM: token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token != JsDocToken.EOL && token != JsDocToken.EOC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); } if (type == null) { type = createJSTypeExpression(newStringNode("number")); } if (!jsdocBuilder.recordEnumParameterType(type)) { parser.addWarning("msg.jsdoc.incompat.type", lineno, charno); } token = eatTokensUntilEOL(token); continue retry; case EXPORT: if (!jsdocBuilder.recordExport()) { parser.addWarning("msg.jsdoc.export", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTERNS: if (!jsdocBuilder.recordExterns()) { parser.addWarning("msg.jsdoc.externs", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case JAVA_DISPATCH: if (!jsdocBuilder.recordJavaDispatch()) { parser.addWarning("msg.jsdoc.javadispatch", stream.

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTENDS: case IMPLEMENTS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); boolean matchingRc = false; if (token == JsDocToken.LC) { token = next(); matchingRc = true; } if (token == JsDocToken.STRING) { Node typeNode = parseAndRecordTypeNameNode( token, lineno, charno, matchingRc); lineno = stream.getLineno(); charno = stream.getCharno(); typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && !matchingRc) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } type = createJSTypeExpression(typeNode); if (annotation == Annotation.EXTENDS) { if (!jsdocBuilder.recordBaseType(type)) { parser.addWarning( "msg.jsdoc.incompat.type", lineno, charno); } } else { Preconditions.checkState( annotation == Annotation.IMPLEMENTS); if (!jsdocBuilder.recordImplementedInterface(type)) { parser.addWarning("msg.jsdoc.implements.duplicate", lineno, charno); } } token = next(); if (matchingRc) { if (token != JsDocToken.RC) { parser.addWarning("msg.jsdoc.missing.rc", stream.getLineno(), stream.getCharno()); } } else if (token != JsDocToken.EOL && token != JsDocToken.EOF && token != JsDocToken.EOC) { parser.addWarning("msg.end.annotation.expected", stream.getLineno(), stream.getCharno()); } } else { parser.addWarning("msg.no.type.name", lineno, charno); } token = eatTokensUntilEOL(token); continue retry; case HIDDEN: if (!jsdocBuilder.recordHiddenness()) { parser.addWarning("msg.jsdoc.hidden", stream.getLineno(), stream.get

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>Charno()); } token = eatTokensUntilEOL(); continue retry; case NO_ALIAS: if (!jsdocBuilder.recordNoAlias()) { parser.addWarning("msg.jsdoc.noalias", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NO_COMPILE: if (!jsdocBuilder.recordNoCompile()) { parser.addWarning("msg.jsdoc.nocompile", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NO_TYPE_CHECK: if (!jsdocBuilder.recordNoTypeCheck()) { parser.addWarning("msg.jsdoc.nocheck", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NOT_IMPLEMENTED: token = eatTokensUntilEOL(); continue retry; case INHERIT_DOC: case OVERRIDE: if (!jsdocBuilder.recordOverride()) { parser.addWarning("msg.jsdoc.override", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case THROWS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } } // *Update* the token to that after the type annotation. token = current(); // Save the throw type. jsdocBuilder.recordThrowType(type); // Find the throw's description (if applicable). ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; if (description.length() > 0) { jsdocBuilder.recordThrowDescription(type, description); } token = descriptionInfo.token; continue retry; case PARAM: skipEOLs(); token

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordParamTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); } String name = null; boolean isBracketedParam = JsDocToken.LB == token; if (isBracketedParam) { token = next(); } if (JsDocToken.STRING != token) { parser.addWarning("msg.missing.variable.name", lineno, charno); } else { name = stream.getString(); if (isBracketedParam) { token = next(); // Throw out JsDocToolkit's "default" parameter annotation. // It makes no sense under our type system. if (JsDocToken.EQUALS == token) { token = next(); if (JsDocToken.STRING == token) { token = next(); } } if (JsDocToken.RB != token) { reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } else if (type != null) { // Make the type expression optional, if it isn't // already. type = JSTypeExpression.makeOptionalArg(type); } } // If the param name has a DOT in it, just throw it out // quietly. We do not handle the JsDocToolkit method // for handling properties of params. if (name.indexOf('.') > -1) { name = null; } else if (!jsdocBuilder.recordParameter(name, type)) { if (jsdocBuilder.hasParameter(name)) { parser.addWarning("msg.dup.variable.name", name, lineno, charno); } else { parser.addWarning("msg.jsdoc.incompat.type", name, lineno, charno); } } } if (name == null) { token = eatTokensUntilEOL(

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>implicitcast", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case SEE: ExtractionInfo referenceInfo = extractSingleLineBlock(); String reference = referenceInfo.string; if (reference.length() == 0) { parser.addWarning("msg.jsdoc.seemissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addReference(reference); } token = referenceInfo.token; continue retry; case SUPPRESS: token = parseSuppressTag(next()); continue retry; case TEMPLATE: ExtractionInfo templateInfo = extractSingleLineBlock(); String templateTypeName = templateInfo.string; if (templateTypeName.length() == 0) { parser.addWarning("msg.jsdoc.templatemissing", stream.getLineno(), stream.getCharno()); } else if (!jsdocBuilder.recordTemplateTypeName( templateTypeName)) { parser.addWarning("msg.jsdoc.template.at.most.once", stream.getLineno(), stream.getCharno()); } token = templateInfo.token; continue retry; case VERSION: ExtractionInfo versionInfo = extractSingleLineBlock(); String version = versionInfo.string; if (version.length() == 0) { parser.addWarning("msg.jsdoc.versionmissing", stream.getLineno(), stream.getCharno()); } else { if (!jsdocBuilder.recordVersion(version)) { parser.addWarning("msg.jsdoc.extraversion", stream.getLineno(), stream.getCharno()); } } token = versionInfo.token; continue retry; case DEFINE: case RETURN: case THIS: case TYPE: case TYPEDEF: skipEOLs(); lineno = stream.getLineno(); charno = stream.getCharno(); token = next(); Node typeNode = parseAndRecordTypeNode(token, lineno, charno); if (annotation == Annotation.THIS) { typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && token != JsDocToken.LC) { typeNode

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>SEARCHING_NEWLINE) { state = State.SEARCHING_ANNOTATION; } token = next(); continue retry; default: if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) { token = next(); continue retry; } else { state = State.SEARCHING_NEWLINE; token = eatTokensUntilEOL(); continue retry; } } // next token token = next(); } } /** * Parse a {@code @suppress} tag of the form * {@code @suppress&#123;warning1|warning2&#125;}. * * @param token The current token. */ private JsDocToken parseSuppressTag(JsDocToken token) { if (token == JsDocToken.LC) { Set<String> suppressions = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!suppressionNames.contains(name)) { parser.addWarning("msg.jsdoc.suppress.unknown", name, stream.getLineno(), stream.getCharno()); } suppressions.add(stream.getString()); token = next(); } else { parser.addWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordSuppressions(suppressions)) { parser.addWarning("msg.jsdoc.suppress.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @return The type expression found or null if none. */ private Node

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case STAR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ private JSTypeExpression createJSTypeExpression(Node n) { return n == null ? null : new JSTypeExpression(n, sourceName); } /** * Tuple for returning both the string extracted and the * new token following a call to any of the extract*Block * methods. */ private static class ExtractionInfo { private final String string; private final JsDocToken token; public ExtractionInfo(String string, JsDocToken token) { this.string = string; this.token = token; } } /** * Extracts the text found on the current line starting at token. Note that * token = token.info; should be called after this method is used to update * the token properly in the parser. * * @return The extraction information. */ private ExtractionInfo extractSingleLineBlock() { // Get the current starting point. stream.update(); int lineno = stream.getLineno(); int charno = stream.getCharno() + 1; String line = stream.getRemainingJSDocLine().trim(); // Record the textual description. if (line.length() > 0) { jsdocBuilder.markText(line, lineno, charno, lineno, charno + line.length()); } return new ExtractionInfo(line, next()); } private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) { return extractMultilineTextual

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>Block(token, WhitespaceOption.SINGLE_LINE); } private enum WhitespaceOption { /** * Preserves all whitespace and formatting. Needed for licenses and * purposely formatted text. */ PRESERVE, /** Preserves newlines but trims the output. */ TRIM, /** Removes newlines and turns the output into a single line string. */ SINGLE_LINE } /** * Extracts the text found on the current line and all subsequent * until either an annotation, end of comment or end of file is reached. * Note that if this method detects an end of line as the first token, it * will quit immediately (indicating that there is no text where it was * expected). Note that token = info.token; should be called after this * method is used to update the token properly in the parser. * * @param token The start token. * @param option How to handle whitespace. * * @return The extraction information. */ @SuppressWarnings("fallthrough") private ExtractionInfo extractMultilineTextualBlock(JsDocToken token, WhitespaceOption option) { if (token == JsDocToken.EOC || token == JsDocToken.EOL || token == JsDocToken.EOF) { return new ExtractionInfo("", token); } stream.update(); int startLineno = stream.getLineno(); int startCharno = stream.getCharno() + 1; // Read the content from the first line. String line = stream.getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = line.trim(); } StringBuilder builder = new StringBuilder(); builder.append(line); state = State.SEARCHING_ANNOTATION; token = next(); boolean ignoreStar = false; do { switch (token) { case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next();

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. if (!(option == WhitespaceOption.PRESERVE && token == JsDocToken.ANNOTATION)) { String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(multilineText, startLineno, startCharno, endLineno, endCharno); } return new ExtractionInfo(multilineText, token); } // FALL THROUGH default: ignoreStar = false; state = State.SEARCHING_ANNOTATION; if (builder.length() > 0) { builder.append(' '); } builder.append(toString(token)); line = stream.getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = trimEnd(line); } builder.append(line); token = next(); } } while (true); } /** * Extracts the top-level block comment from the JsDoc comment, if any. * This method differs from the extractMultilineTextualBlock in that it * terminates under different conditions (it doesn't have the same * prechecks), it does not first read in the remaining of the current * line and its conditions for ignoring the "*" (STAR) are different. * * @param token The starting token. * * @return The extraction information. */ private ExtractionInfo extractBlockComment(JsDocToken token) { StringBuilder builder = new StringBuilder(); boolean ignoreStar = true; do { switch (token) { case ANNOTATION: case EOC: case EOF: return new ExtractionInfo(builder.toString().trim(), token); case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(' '); } builder.append('*'); }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> token = next(); continue; case EOL: ignoreStar = true; builder.append('\n'); token = next(); continue; default: if (!ignoreStar && builder.length() > 0) { builder.append(' '); } ignoreStar = false; builder.append(toString(token)); String line = stream.getRemainingJSDocLine(); line = trimEnd(line); builder.append(line); token = next(); } } while (true); } /** * Trim characters from only the end of a string. * This method will remove all whitespace characters * (defined by Character.isWhitespace(char), in addition to the characters * provided, from the end of the provided string. * * @param s String to be trimmed * @return String with whitespace and characters in extraChars removed * from the end. */ private static String trimEnd(String s) { int trimCount = 0; while (trimCount < s.length()) { char ch = s.charAt(s.length() - trimCount - 1); if (Character.isWhitespace(ch)) { trimCount++; } else { break; } } if (trimCount == 0) { return s; } return s.substring(0, s.length() - trimCount); } // Based on ES4 grammar proposed on July 10, 2008. // http://wiki.ecmascript.org/doku.php?id=spec:spec // Deliberately written to line up with the actual grammar rules, // for maximum flexibility. // TODO(nicksantos): The current implementation tries to maintain backwards // compatibility with previous versions of the spec whenever we can. // We should try to gradually withdraw support for these. /** * TypeExpressionAnnotation := TypeExpression | * '{' TopLevelTypeExpression '}' */ private Node parseTypeExpressionAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTopLevelTypeExpression(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) {

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeExpression(token); } } /** * ParamTypeExpressionAnnotation := * '{' OptionalParameterType '}' | * '{' TopLevelTypeExpression '}' | * '{' '...' TopLevelTypeExpression '}' * * OptionalParameterType := * TopLevelTypeExpression '=' */ private Node parseParamTypeExpressionAnnotation(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); skipEOLs(); boolean restArg = false; token = next(); if (token == JsDocToken.ELLIPSIS) { token = next(); if (token == JsDocToken.RC) { // EMPTY represents the UNKNOWN type in the Type AST. return wrapNode(Token.ELLIPSIS, new Node(Token.EMPTY)); } restArg = true; } Node typeNode = parseTopLevelTypeExpression(token); if (typeNode != null) { skipEOLs(); if (restArg) { typeNode = wrapNode(Token.ELLIPSIS, typeNode); } else if (match(JsDocToken.EQUALS)) { next(); skipEOLs(); typeNode = wrapNode(Token.EQUALS, typeNode); } if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } /** * TypeNameAnnotation := TypeName | '{' TypeName '}' */ private Node parseTypeNameAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTypeName(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeName(token); } } /** * TopLevelTypeExpression := TypeExpression * | Type

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>UnionList * * We made this rule up, for the sake of backwards compatibility. */ private Node parseTopLevelTypeExpression(JsDocToken token) { Node typeExpr = parseTypeExpression(token); if (typeExpr != null) { // top-level unions are allowed if (match(JsDocToken.PIPE)) { next(); if (match(JsDocToken.PIPE)) { // We support double pipes for backwards-compatibility. next(); } skipEOLs(); token = next(); return parseUnionTypeWithAlternate(token, typeExpr); } } return typeExpr; } /** * TypeExpressionList := TopLevelTypeExpression * | TopLevelTypeExpression ',' TypeExpressionList */ private Node parseTypeExpressionList(JsDocToken token) { Node typeExpr = parseTopLevelTypeExpression(token); if (typeExpr == null) { return null; } Node typeList = new Node(Token.BLOCK); typeList.addChildToBack(typeExpr); while (match(JsDocToken.COMMA)) { next(); skipEOLs(); typeExpr = parseTopLevelTypeExpression(next()); if (typeExpr == null) { return null; } typeList.addChildToBack(typeExpr); } return typeList; } /** * TypeExpression := BasicTypeExpression * | '?' BasicTypeExpression * | '!' BasicTypeExpression * | BasicTypeExpression '?' * | BasicTypeExpression '!' * | '?' */ private Node parseTypeExpression(JsDocToken token) { if (token == JsDocToken.QMARK) { // A QMARK could mean that a type is nullable, or that it's unknown. // We use look-ahead 1 to determine whether it's unknown. Otherwise, // we assume it means nullable. There are 5 cases: // {?} - right curly // {?=} - equals // {function(?, number)} - comma // {function(number, ?)} - right paren // {function(): ?|number} - pipe // I'm not a big fan of using look-ahead for this, but it makes // the type language a lot nicer. token = next

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>(); if (token == JsDocToken.COMMA || token == JsDocToken.EQUALS || token == JsDocToken.RC || token == JsDocToken.RP || token == JsDocToken.PIPE) { restoreLookAhead(token); return newNode(Token.QMARK); } return wrapNode(Token.QMARK, parseBasicTypeExpression(token)); } else if (token == JsDocToken.BANG) { return wrapNode(Token.BANG, parseBasicTypeExpression(next())); } else { Node basicTypeExpr = parseBasicTypeExpression(token); if (basicTypeExpr != null) { if (match(JsDocToken.QMARK)) { next(); return wrapNode(Token.QMARK, basicTypeExpr); } else if (match(JsDocToken.BANG)) { next(); return wrapNode(Token.BANG, basicTypeExpr); } } return basicTypeExpr; } } /** * BasicTypeExpression := '*' | 'null' | 'undefined' | TypeName * | FunctionType | UnionType | RecordType | ArrayType */ private Node parseBasicTypeExpression(JsDocToken token) { if (token == JsDocToken.STAR) { return newNode(Token.STAR); } else if (token == JsDocToken.LB) { skipEOLs(); return parseArrayType(next()); } else if (token == JsDocToken.LC) { skipEOLs(); return parseRecordType(next()); } else if (token == JsDocToken.LP) { skipEOLs(); return parseUnionType(next()); } else if (token == JsDocToken.STRING) { String string = stream.getString(); if ("function".equals(string)) { skipEOLs(); return parseFunctionType(next()); } else if ("null".equals(string) || "undefined".equals(string)) { return newStringNode(string); } else { return parseTypeName(token); } } return reportGenericTypeSyntaxWarning(); } /** * TypeName := NameExpression | NameExpression TypeApplication * TypeApplication := '.<' TypeExpressionList '>' * TypeExpressionList := TypeExpression // a white lie */

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> private Node parseTypeName(JsDocToken token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } Node typeName = newStringNode(stream.getString()); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeName.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeName; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType := * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType */ private Node parseFunctionType(JsDocToken token) { // NOTE(nicksantos): We're not implementing generics at the moment, so // just throw out TypeParameters. if (token != JsDocToken.LP) { return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); } Node functionType = newNode(Token.FUNCTION); Node parameters = null; skipEOLs(); if (!match(JsDocToken.RP)) { token = next(); boolean hasParams = true; if (token == JsDocToken.STRING && "this".equals(stream.getString())) { if (match(JsDocToken.COLON)) { next(); skipEOLs(); Node thisType = wrapNode(Token.THIS, parseTypeName(next())); if (thisType == null) { return null; } functionType.addChildToFront(thisType); } else { return reportTypeSyntaxWarning("msg.jsdoc.missing.colon"); } if (match(JsDocToken.COMMA)) { next(); skipEOLs(); token = next(); } else { hasParams = false; } } if (hasParams) { parameters = parseParametersType(token); if (parameters == null) { return null; } } } if (parameters != null) { functionType.addChildToBack(parameters); } skipEOLs(); if (!match(Js

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>DocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } skipEOLs(); Node resultType = parseResultType(next()); if (resultType == null) { return null; } else { functionType.addChildToBack(resultType); } return functionType; } /** * ParametersType := RestParameterType | NonRestParametersType * | NonRestParametersType ',' RestParameterType * RestParameterType := '...' Identifier * NonRestParametersType := ParameterType ',' NonRestParametersType * | ParameterType * | OptionalParametersType * OptionalParametersType := OptionalParameterType * | OptionalParameterType, OptionalParametersType * OptionalParameterType := ParameterType= * ParameterType := TypeExpression | Identifier ':' TypeExpression */ // NOTE(nicksantos): The official ES4 grammar forces optional and rest // arguments to come after the required arguments. Our parser does not // enforce this. Instead we allow them anywhere in the function at parse-time, // and then warn about them during type resolution. // // In theory, it might be mathematically nicer to do the order-checking here. // But in practice, the order-checking for structural functions is exactly // the same as the order-checking for @param annotations. And the latter // has to happen during type resolution. Rather than duplicate the // order-checking in two places, we just do all of it in type resolution. private Node parseParametersType(JsDocToken token) { Node paramsType = newNode(Token.LP); boolean isVarArgs = false; Node paramType = null; if (token != JsDocToken.RP) { do { if (paramType != null) { // skip past the comma next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { // In the latest ES4 proposal, there are no type constraints allowed // on variable arguments. We support the old syntax for backwards // compatibility, but we should gradually tear it out. skipEOLs(); if (match(JsDocToken.RP)) { paramType = newNode(Token.ELLIPSIS); } else { skipEOLs(); if (!match(

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>JsDocToken.LB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.lb"); } next(); skipEOLs(); paramType = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); skipEOLs(); if (!match(JsDocToken.RB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } skipEOLs(); next(); } isVarArgs = true; } else { paramType = parseTypeExpression(token); if (match(JsDocToken.EQUALS)) { skipEOLs(); next(); paramType = wrapNode(Token.EQUALS, paramType); } } if (paramType == null) { return null; } paramsType.addChildToBack(paramType); if (isVarArgs) { break; } } while (match(JsDocToken.COMMA)); } if (isVarArgs && match(JsDocToken.COMMA)) { return reportTypeSyntaxWarning("msg.jsdoc.function.varargs"); } // The right paren will be checked by parseFunctionType return paramsType; } /** * ResultType := <empty> | ':' void | ':' TypeExpression */ private Node parseResultType(JsDocToken token) { skipEOLs(); if (!match(JsDocToken.COLON)) { return newNode(Token.EMPTY); } token = next(); skipEOLs(); if (match(JsDocToken.STRING) && "void".equals(stream.getString())) { next(); return newNode(Token.VOID); } else { return parseTypeExpression(next()); } } /** * UnionType := '(' TypeUnionList ')' * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList * * We've removed the empty union type. */ private Node parseUnionType(JsDocToken token) { return parseUnionTypeWithAlternate(token, null); } /** * Create a new union type, with an alternate that has already been * parsed. The alternate may be null. */ private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { Node union = newNode(Token.

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>PIPE); if (alternate != null) { union.addChildToBack(alternate); } Node expr = null; do { if (expr != null) { skipEOLs(); token = next(); Preconditions.checkState( token == JsDocToken.PIPE || token == JsDocToken.COMMA); boolean isPipe = token == JsDocToken.PIPE; if (isPipe && match(JsDocToken.PIPE)) { // We support double pipes for backwards compatiblity. next(); } skipEOLs(); token = next(); } expr = parseTypeExpression(token); if (expr == null) { return null; } union.addChildToBack(expr); // We support commas for backwards compatiblity. } while (match(JsDocToken.PIPE, JsDocToken.COMMA)); if (alternate == null) { skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } next(); } return union; } /** * ArrayType := '[' ElementTypeList ']' * ElementTypeList := <empty> | TypeExpression | '...' TypeExpression * | TypeExpression ',' ElementTypeList */ private Node parseArrayType(JsDocToken token) { Node array = newNode(Token.LB); Node arg = null; boolean hasVarArgs = false; do { if (arg != null) { next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { arg = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); hasVarArgs = true; } else { arg = parseTypeExpression(token); } if (arg == null) { return null; } array.addChildToBack(arg); if (hasVarArgs) { break; } skipEOLs(); } while (match(JsDocToken.COMMA)); if (!match(JsDocToken.RB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } next(); return array; } /** * RecordType := '{' FieldTypeList '}' */

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> private Node parseRecordType(JsDocToken token) { Node recordType = newNode(Token.LC); Node fieldTypeList = parseFieldTypeList(token); if (fieldTypeList == null) { return reportGenericTypeSyntaxWarning(); } skipEOLs(); if (!match(JsDocToken.RC)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } next(); recordType.addChildToBack(fieldTypeList); return recordType; } /** * FieldTypeList := FieldType | FieldType ',' FieldTypeList */ private Node parseFieldTypeList(JsDocToken token) { Node fieldTypeList = newNode(Token.LB); do { Node fieldType = parseFieldType(token); if (fieldType == null) { return null; } fieldTypeList.addChildToBack(fieldType); skipEOLs(); if (!match(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ?

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> null : new Node(type, n, stream.getLineno(), stream.getCharno()); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s) { return Node.newString(s, stream.getLineno(), stream.getCharno()); } private Node reportTypeSyntaxWarning(String warning) { parser.addWarning(warning, stream.getLineno(), stream.getCharno()); return null; } private Node reportGenericTypeSyntaxWarning() { return reportTypeSyntaxWarning("msg.jsdoc.type.syntax"); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */ private JsDocToken eatTokensUntilEOL() { return eatTokensUntilEOL(next()); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */ private JsDocToken eatTokensUntilEOL(JsDocToken token) { do { if (token == JsDocToken.EOL || token == JsDocToken.EOC || token == JsDocToken.EOF) { state = State.SEARCHING_ANNOTATION; return token; } token = next(); } while (true); } /** * Specific value indicating that the {@link #unreadToken} contains no token. */ private static final JsDocToken NO_UNREAD_TOKEN = null; /** * One token buffer. */ private JsDocToken unreadToken = NO_UNREAD_TOKEN; /** Restores the lookahead token to the token stream */ private void restoreLookAhead(JsDocToken token) { unreadToken = token; } /** * Tests whether the next symbol of the token stream matches the specific * token. */ private boolean match(JsDocToken token) { unreadToken = next(); return unreadToken == token; } /** * Tests that the next symbol of the token stream matches one of the specified * tokens. */ private boolean match(JsDoc

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>Token token1, JsDocToken token2) { unreadToken = next(); return unreadToken == token1 || unreadToken == token2; } /** * Gets the next token of the token stream or the buffered token if a matching * was previously made. */ private JsDocToken next() { if (unreadToken == NO_UNREAD_TOKEN) { return stream.getJsDocToken(); } else { return current(); } } /** * Gets the current token, invalidating it in the process. */ private JsDocToken current() { JsDocToken t = unreadToken; unreadToken = NO_UNREAD_TOKEN; return t; } /** * Skips all EOLs and all empty lines in the JSDoc. Call this method if you * want the JSDoc entry to span multiple lines. */ private void skipEOLs() { while (match(JsDocToken.EOL)) { next(); if (match(JsDocToken.STAR)) { next(); } } } /** * Determines whether the parser has been populated with docinfo with a * fileoverview tag. */ private boolean hasParsedFileOverviewDocInfo() { return jsdocBuilder.isPopulatedWithFileOverview(); } boolean hasParsedJSDocInfo() { return jsdocBuilder.isPopulated(); } JSDocInfo retrieveAndResetParsedJSDocInfo() { return jsdocBuilder.build(sourceName); } /** * Gets the fileoverview JSDocInfo, if any. */ JSDocInfo getFileOverviewJSDocInfo() { return fileOverviewJSDocInfo; } }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name) node); case Token.NEW: return processNewExpression((NewExpression) node); case Token.NUMBER: return processNumberLiteral((NumberLiteral) node); case Token.OBJECTLIT: return processObjectLiteral((ObjectLiteral) node); case Token.REGEXP: return processRegExpLiteral((RegExpLiteral) node); case Token.RETURN: return processReturnStatement((ReturnStatement) node); case Token.SCRIPT: return processAstRoot((AstRoot) node); case Token.STRING: return processStringLiteral((StringLiteral) node); case Token.SWITCH: return processSwitchStatement((SwitchStatement) node); case Token.THROW: return processThrowStatement((ThrowStatement) node); case Token.TRY: return processTryStatement((TryStatement) node); case Token.VAR: if (node instanceof VariableDeclaration) { return processVariableDeclaration((VariableDeclaration) node); } else if (node instanceof VariableInitializer) { return processVariableInitializer((VariableInitializer) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.WHILE: return processWhileLoop((WhileLoop) node); case Token.WITH: return processWithStatement((WithStatement) node); } return processIllegalToken(node); } }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> * Gets the boolean value of a node that represents a literal. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function. * * @throws IllegalArgumentException If {@code n} is not a literal value */ static TernaryValue getBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NULL: case Token.FALSE: case Token.VOID: return TernaryValue.FALSE; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: return TernaryValue.TRUE; } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): Convert constant array, object, and regex literals as well. switch (n.getType()) { case Token.NAME: case Token.STRING: return n.getString(); case Token.NUMBER: double value = n.getDouble(); long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(n.getDouble()); } case Token.FALSE: case Token.TRUE: case Token.NULL: return Node.tokenToName(n.getType()); case Token.VOID: return "undefined"; } return null;

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> * where it is evaluated. So /xyz/ and [3, 5] are literals, but * function() { return a; } is not. */ static boolean isLiteralValue(Node n) { // TODO(nicksantos): Refine this function to catch more literals. switch (n.getType()) { case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child)) { return false; } } return true; default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Single operators are valid if the child is valid. case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.NOT: case Token.NEG: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (block.getType() != Token.BLOCK) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (n.getType() != Token.EMPTY) { return false; } } return true; } /** * A "simple" operator is one whose children are expressions, * has no direct side

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> /** * Returns true if some node in n's subtree changes application state. * If {@code checkForNewObjects} is true, we assume that newly created * mutable objects (like object literals) change state. Otherwise, we assume * that they have no side effects. */ private static boolean checkForStateChangeHelper( Node n, boolean checkForNewObjects, AbstractCompiler compiler) { // Rather than id which ops may have side effects, id the ones // that we know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.LP: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) return true; break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.ARRAYLIT: case Token.CALL: case Token.EMPTY: case Token.FALSE: case Token.FUNCTION: case Token.GETELEM: case Token.GETPROP: case Token.GET_REF: case Token.IF: case Token.LP: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.RETURN: case Token.STRING: case Token.THIS: case Token.TRUE: return 15; default: throw new Error("Unknown precedence for " + Node.tokenToName(type) + " (type " + type + ")"); } } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatentation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BIT

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> Token.BLOCK); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else { return false; } } /** * Is this a CALL node? */ static boolean isCall(Node n) { return n.getType() == Token.CALL; } /** * Is this a FUNCTION node? */ static boolean isFunction(Node n) { return n.getType() == Token.FUNCTION; } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(isFunction(fn)); return fn.getLastChild(); } /** * Is this a THIS node? */ static boolean isThis(Node node) { return node.getType() == Token.THIS; } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.getType() == Token.FUNCTION && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().getType() == Token.SCRIPT || n.getParent().getParent().getType() == Token.FUNCTION); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) { return true; } } return false; } /** * Determines whether this node is strictly on the left hand side of an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isLhs(Node n, Node parent) { return (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) || parent.getType() == Token.VAR; } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { if (node.getType() == Token.STRING && parent.getType() == Token.OBJECTLIT) { int index = 0; for (Node current = parent.getFirstChild(); current != null; current = current.getNext()) { if (current == node) { return index % 2 == 0; } index++; } } return false; } /** * Converts an operator's token value (see {@link Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>0" */ static Node newUndefinedNode(Node srcReferenceNode) { // TODO(johnlenz): Why this instead of the more common "undefined"? Node node = new Node(Token.VOID, Node.newNumber(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.copyInformationFrom(value); } Node var = new Node(Token.VAR, nodeName) .copyInformationFrom(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } public boolean apply(Node n) { return n.getType() == Token.NAME && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { public boolean apply(Node n) { return isFunctionDeclaration(n) || n.getType() == Token.VAR; } } /** * A predicate for matching anything except function nodes. */ static class MatchNotFunction implements Predicate<Node>{ public boolean apply(Node n) { return !isFunction(n); } } /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ public boolean apply(Node n) { Node parent = n.getParent(); return n.getType() ==

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>, and non-controversial. #3 is * a bit trickier. It means that if you have: * <code> * /** @param {number} x / * Foo.prototype.bar = goog.abstractMethod; * </code> * the JSDocInfo will appear in two places in the type system: in the 'bar' * slot of Foo.prototype, and on the function expression type created by * this expression. * * @author nicksantos@google.com (Nick Santos) */ class InferJSDocInfo extends AbstractPostOrderCallback implements CompilerPass { private AbstractCompiler compiler; private boolean inExterns; InferJSDocInfo(AbstractCompiler compiler) { this.compiler = compiler; } public void process(Node externs, Node root) { if (externs != null) { inExterns = true; NodeTraversal.traverse(compiler, externs, this); } if (root != null) { inExterns = false; NodeTraversal.traverse(compiler, root, this); } } public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo docInfo; switch (n.getType()) { // Infer JSDocInfo on types of all type declarations on variables. case Token.NAME: if (parent == null) { return; } // Only allow JSDoc on VARs, function declarations, and assigns. if (parent.getType() != Token.VAR && !NodeUtil.isFunctionDeclaration(parent) && !(parent.getType() == Token.ASSIGN && n == parent.getFirstChild())) { return; } // There are four places the doc info could live. // 1) A FUNCTION node. // /** ... */ function f() { ... } // 2) An ASSIGN parent. // /** ... */ x = function () { ... } // 3) A NAME parent. // var x, /** ... */ y = function() { ... } // 4) A VAR gramps. // /** ... */ var x = function() { ... } docInfo = n.getJSDocInfo(); if (docInfo == null &&

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_VOID: throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT."); case Token.EXPR_RESULT: Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parens. Otherwise, when parsed, NEW will bind to the // first viable parens if (NodeUtil.containsCall(first)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING: Preconditions.checkState(childCount == 0); add(jsString(n.getString(), outputCharsetEncoder)); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { Preconditions.checkState(childCount % 2 == 0); boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> MAX_UNION_SIZE) { return this; } // Look through the alternates we've got so far, // and check if any of them are duplicates of // one another. Iterator<JSType> it = alternates.iterator(); while (it.hasNext()) { JSType current = it.next(); if (alternate.isUnknownType() || current.isUnknownType()) { if (alternate.isEquivalentTo(current)) { // Alternate is unnecessary. return this; } } else { if (alternate.isSubtype(current)) { // Alternate is unnecessary. return this; } else if (current.isSubtype(alternate)) { // Alternate makes current obsolete it.remove(); } } } alternates.add(alternate); result = null; // invalidate the memoized result } } else { result = null; } return this; } /** * Creates a union. * @return A UnionType if it has two or more alternates, the * only alternate if it has one and otherwise {@code NO_TYPE}. */ JSType build() { if (result == null) { if (isAllType) { result = registry.getNativeType(ALL_TYPE); } else if (isNativeUnknownType) { if (areAllUnknownsChecked) { result = registry.getNativeType(CHECKED_UNKNOWN_TYPE); } else { result = registry.getNativeType(UNKNOWN_TYPE); } } else { int size = alternates.size(); if (size > MAX_UNION_SIZE) { result = registry.getNativeType(UNKNOWN_TYPE); } else { if (size > 1) { result = new UnionType(registry, getAlternateListCopy()); } else if (size == 1) { result = alternates.iterator().next(); } else { result = registry.getNativeType(NO_TYPE); } } } } return result; } private static final Comparator<JSType> typeSorter = new Comparator<JSType>() { @Override public int compare(JSType a, JSType b) { return b.hashCode() - a.hashCode();

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> the function that we're assigning to. * If null, that just means we're not initializing this to a function * literal. */ FunctionTypeBuilder inferFromOverriddenFunction( @Nullable FunctionType oldType, @Nullable Node paramsParent) { if (oldType == null) { return this; } returnType = oldType.getReturnType(); returnTypeInferred = oldType.isReturnTypeInferred(); if (paramsParent == null) { // Not a function literal. parametersNode = oldType.getParametersNode(); if (parametersNode == null) { parametersNode = new FunctionParamBuilder(typeRegistry).build(); } } else { // We're overriding with a function literal. Apply type information // to each parameter of the literal. FunctionParamBuilder paramBuilder = new FunctionParamBuilder(typeRegistry); Iterator<Node> oldParams = oldType.getParameters().iterator(); boolean warnedAboutArgList = false; boolean oldParamsNodeHasVarArgs = false; for (Node currentParam = paramsParent.getFirstChild(); currentParam != null; currentParam = currentParam.getNext()) { if (oldParams.hasNext()) { Node oldParam = oldParams.next(); Node newParam = paramBuilder.newParameterFromNode(oldParam); // The subclass method might right its var_args as individual // arguments. if (currentParam.getNext() != null && newParam.isVarArgs()) { newParam.setVarArgs(false); newParam.setOptionalArg(true); oldParamsNodeHasVarArgs = true; } } else { warnedAboutArgList |= addParameter( paramBuilder, typeRegistry.getNativeType(UNKNOWN_TYPE), warnedAboutArgList, codingConvention.isOptionalParameter(currentParam) || oldParamsNodeHasVarArgs, codingConvention.isVarArgsParameter(currentParam)); } } parametersNode = paramBuilder.build(); } return this; } /** * Infer the return type from JSDocInfo. */ FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info) { if (info != null && info.hasReturnType()) { returnType = info.getReturnType().evaluate(scope, typeRegistry); returnTypeInferred =

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>BadModuleReference(name, ref); } } } } } private void reportBadModuleReference(Name name, Ref ref) { compiler.report( JSError.make(ref.sourceName, ref.node, STRICT_MODULE_DEP_QNAME, ref.module.getName(), name.declaration.module.getName(), name.fullName())); } private void reportRefToUndefinedName(Name name, Ref ref) { // grab the highest undefined ancestor to output in the warning message. while (name.parent != null && name.parent.globalSets + name.parent.localSets == 0) { name = name.parent; } // If this is an annotated EXPR-GET, don't do anything. Node parent = ref.node.getParent(); if (parent.getType() == Token.EXPR_RESULT) { JSDocInfo info = ref.node.getJSDocInfo(); if (info != null && info.hasTypedefType()) { return; } } compiler.report( JSError.make(ref.sourceName, ref.node, level, UNDEFINED_NAME_WARNING, name.fullName())); } /** * Checks whether the given name is a property, and whether that property * must be initialized with its full qualified name. */ private static boolean propertyMustBeInitializedByFullName(Name name) { // If an object literal in the global namespace is never aliased, // then all of its properties must be defined using its full qualified // name. This implies that its properties must all be in the global // namespace as well. // // The same is not true for FUNCTION and OTHER types, because their // implicit prototypes have properties that are not captured by the global // namespace. return name.parent != null && name.parent.aliasingGets == 0 && name.parent.type == Name.Type.OBJECTLIT; } }

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>SingletonGetter".equals(callName)) || callNode.getChildCount() != 2) { return null; } return callArg.getNext().getQualifiedName(); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { functionType.defineDeclaredProperty("getInstance", getterType, false); functionType.defineDeclaredProperty("instance_", objectType, false); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.getType() == Token.CALL); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()); } @Override public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t, Node callNode) { Preconditions.checkArgument(callNode.getType() == Token.CALL); Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callName.getChildCount() != 2) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (objectNode.getType() != Token.OBJECTLIT) { t.getCompiler().report(JSError.make(t.getSourceName(), callNode, OBJECTLIT_EXPECTED)); return null; } return new ObjectLiteralCast(typeNode.getQualifiedName(), typeNode.getNext()); } @Override public boolean isOptionalParameter(Node parameter) { return false; } @Override public boolean isVarArgsParameter(Node parameter) { return false; } @Override public boolean isPrivate(String name) { return false; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return ImmutableList.<AssertionFunctionSpec>

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> The * QUOTED_PROP int property is only assigned to STRING tokens used as * object lit keys. * @return true if this was a quoted string key in an object literal. */ @Override public boolean isQuotedString() { return getBooleanProp(QUOTED_PROP); } /** * This should only be called for STRING nodes created in object lits. */ @Override public void setQuotedString() { putBooleanProp(QUOTED_PROP, true); } private String str; } // PropListItems are immutable so that they can be shared. private static class PropListItem implements Serializable { private static final long serialVersionUID = 1L; final PropListItem next; final int type; final int intValue; final Object objectValue; PropListItem(int type, int intValue, PropListItem next) { this(type, intValue, null, next); } PropListItem(int type, Object objectValue, PropListItem next) { this(type, 0, objectValue, next); } PropListItem( int type, int intValue, Object objectValue, PropListItem next) { this.type = type; this.intValue = intValue; this.objectValue = objectValue; this.next = next; } } public Node(int nodeType) { type = nodeType; parent = null; sourcePosition = -1; } public Node(int nodeType, Node child) { Preconditions.checkArgument(child.parent == null, "new child has existing parent"); Preconditions.checkArgument(child.next == null, "new child has existing sibling"); type = nodeType; parent = null; first = last = child; child.next = null; child.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node right) { Preconditions.checkArgument(left.parent == null, "first new child has existing parent"); Preconditions.checkArgument(left.next == null, "first new child has existing sibling"); Preconditions.checkArgument(right.parent == null, "second new child has existing parent"); Preconditions.checkArgument(right.next == null, "second

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> new child has existing sibling"); type = nodeType; parent = null; first = left; last = right; left.next = right; left.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node mid, Node right) { Preconditions.checkArgument(left.parent == null); Preconditions.checkArgument(left.next == null); Preconditions.checkArgument(mid.parent == null); Preconditions.checkArgument(mid.next == null); Preconditions.checkArgument(right.parent == null); Preconditions.checkArgument(right.next == null); type = nodeType; parent = null; first = left; last = right; left.next = mid; left.parent = this; mid.next = right; mid.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node mid, Node mid2, Node right) { Preconditions.checkArgument(left.parent == null); Preconditions.checkArgument(left.next == null); Preconditions.checkArgument(mid.parent == null); Preconditions.checkArgument(mid.next == null); Preconditions.checkArgument(mid2.parent == null); Preconditions.checkArgument(mid2.next == null); Preconditions.checkArgument(right.parent == null); Preconditions.checkArgument(right.next == null); type = nodeType; parent = null; first = left; last = right; left.next = mid; left.parent = this; mid.next = mid2; mid.parent = this; mid2.next = right; mid2.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, int lineno, int charno) { type = nodeType; parent = null; sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node child, int lineno, int charno) { this(nodeType, child); sourcePosition = merge

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>LineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node right, int lineno, int charno) { this(nodeType, left, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node right, int lineno, int charno) { this(nodeType, left, mid, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node mid2, Node right, int lineno, int charno) { this(nodeType, left, mid, mid2, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children, int lineno, int charno) { this(nodeType, children); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children) { this.type = nodeType; parent = null; if (children.length != 0) { this.first = children[0]; this.last = children[children.length - 1]; for (int i = 1; i < children.length; i++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument(children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean hasChildren() { return first != null; } public Node getFirstChild() { return first; } public Node getLastChild() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) { return null; } Node n = first; while (n.next != child) { n = n.next; if (n == null) { throw new RuntimeException("node is not a child"); } } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public Node getLastSibling() { Node n = this; while (n.next != null) { n = n.next; } return n; } public void addChildToFront(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = first; first = child; if (last == null) { last = child; } } public void addChildToBack(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = null; if (last == null) { first = last = child; return; } last.next =

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> child; last = child; } public void addChildrenToFront(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } Node lastSib = children.getLastSibling(); lastSib.next = first; first = children; if (last == null) { last = lastSib; } } public void addChildrenToBack(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } if (last != null) { last.next = children; } last = children.getLastSibling(); if (first == null) { first = children; } } /** * Add 'child' before 'node'. */ public void addChildBefore(Node newChild, Node node) { Preconditions.checkArgument(node != null, "The existing child node of the parent should not be null."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); if (first == node) { newChild.parent = this; newChild.next = first; first = newChild; return; } Node prev = getChildBefore(node); addChildAfter(newChild, prev); } /** * Add 'child' after 'node'. */ public void addChildAfter(Node newChild, Node node) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); newChild.parent = this; newChild.next = node.next; node.next = newChild; if (last == node) { last = newChild; } } /** * Detach a child from its parent and siblings. */ public void removeChild(Node child) { Node prev = getChild

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>Before(child); if (prev == null) first = first.next; else prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; } /** * Detaches child from Node and replaces it with newChild. */ public void replaceChild(Node child, Node newChild) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(child); newChild.next = child.next; newChild.parent = this; if (child == first) { first = newChild; } else { Node prev = getChildBefore(child); prev.next = newChild; } if (child == last) last = newChild; child.next = null; child.parent = null; } public void replaceChildAfter(Node prevChild, Node newChild) { Preconditions.checkArgument(prevChild.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(prevChild); Node child = prevChild.next; newChild.next = child.next; newChild.parent = this; prevChild.next = newChild; if (child == last) last = newChild; child.next = null; child.parent = null; } @VisibleForTesting PropListItem lookupProperty(int propType) { PropListItem x = propListHead; while (x != null && propType != x.type) { x = x.next; } return x; } /** * Clone the properties from the provided node without copying * the property object. The recieving node may not have any * existing properties. * @param other The node to clone properties from. *

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> @return this node. */ public Node clonePropsFrom(Node other) { Preconditions.checkState(this.propListHead == null, "Node has existing properties."); this.propListHead = other.propListHead; return this; } public void removeProp(int propType) { PropListItem result = removeProp(propListHead, propType); if (result != propListHead) { propListHead = result; } } /** * @param item The item to inspect * @param propType The property to look for * @return The replacement list if the property was removed, or * 'item' otherwise. */ private PropListItem removeProp(PropListItem item, int propType) { if (item == null) { return null; } else if (item.type == propType) { return item.next; } else { PropListItem result = removeProp(item.next, propType); if (result != item.next) { return new PropListItem( item.type, item.intValue, item.objectValue, result); } else { return item; } } } public Object getProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return null; } return item.objectValue; } public boolean getBooleanProp(int propType) { return getIntProp(propType) != 0; } /** * Returns the integer value for the property, or 0 if the property * is not defined. */ public int getIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return 0; } return item.intValue; } public int getExistingIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { Kit.codeBug(); } return item.intValue; } public void putProp(int propType, Object value) { removeProp(propType); if (value != null) { propListHead = new PropListItem(propType, value, propListHead); } } public void

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> putBooleanProp(int propType, boolean value) { putIntProp(propType, value ? 1 : 0); } public void putIntProp(int propType, int value) { removeProp(propType); if (value != 0) { propListHead = new PropListItem(propType, value, propListHead); } } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.next) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.next) { count--; keys[count] = x.type; } Arrays.sort(keys); return keys; } public int getLineno() { return extractLineno(sourcePosition); } public int getCharno() { return extractCharno(sourcePosition); } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() ==

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { if (Token.printTrees) { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where lines might * be longer than 4096 characters. */ public static final int COLUMN_BITS = 12; /** * MAX_COLUMN_NUMBER represents the maximum column number that can * be represented. JSCompiler's modifications to Rhino cause all * tokens located beyond the maximum column to MAX_COLUMN_NUMBER. */ public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1; /** * COLUMN_MASK stores a value where bits storing the column number * are set, and bits storing the line are not set. It's handy for * separating column number from line number. */ public static final int COLUMN_MASK = MAX_COLUMN_NUMBER; /** * Source position of this node. The position is encoded with the * column number in the low 12 bits of the integer, and

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> nodes's children. * The iterator does not support the optional operation * {@link Iterator#remove()}.</p> * * <p>To iterate over a node's siblings, one can write</p> * <pre>Node n = ...; * for (Node child : n.children()) { ...</pre> */ public Iterable<Node> children() { if (first == null) { return Collections.emptySet(); } else { return new SiblingNodeIterable(first); } } /** * <p>Return an iterable object that iterates over this nodes's siblings. * The iterator does not support the optional operation * {@link Iterator#remove()}.</p> * * <p>To iterate over a node's siblings, one can write</p> * <pre>Node n = ...; * for (Node sibling : n.siblings()) { ...</pre> */ public Iterable<Node> siblings() { return new SiblingNodeIterable(this); } /** * @see Node#siblings() */ private static final class SiblingNodeIterable implements Iterable<Node>, Iterator<Node> { private final Node start; private Node current; private boolean used; SiblingNodeIterable(Node start) { this.start = start; this.current = start; this.used = false; } public Iterator<Node> iterator() { if (!used) { used = true; return this; } else { // We have already used the current object as an iterator; // we must create a new SiblingNodeIterable based on this // iterable's start node. // // Since the primary use case for Node.children is in for // loops, this branch is extremely unlikely. return (new SiblingNodeIterable(start)).iterator(); } } public boolean hasNext() { return current != null; } public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } public void remove() { throw new UnsupportedOperationException(); } } // ========================================================================== // Accessors public Node getParent() { return parent;

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> } /** * Gets the ancestor node relative to this. * * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while (node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable<Node> { private Node cur; /** * @param cur The node to start. */ AncestorIterable(Node cur) { this.cur = cur; } public Iterator<Node> iterator() { return new Iterator<Node>() { public boolean hasNext() { return cur != null; } public Node next() { if (!hasNext()) throw new NoSuchElementException(); Node n = cur; cur = cur.getParent(); return n; } public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Check for one child more efficiently than by iterating over all the * children as is done with Node.getChildCount(). * * @return Whether the node has exactly one child. */ public boolean hasOneChild() { return first != null && first == last; } /** * Check for more than one child more efficiently than by iterating over all * the children as is done with Node.getChildCount(). * * @return Whether the node more than one child. */ public boolean hasMoreThanOneChild() { return first != null && first != last; } public int getChildCount() { int c = 0; for (Node n = first; n != null; n = n.next) c++; return c; } // Intended for testing and verification only. public boolean hasChild(Node child) { for (Node n = first; n != null; n = n.getNext()) { if (child == n) { return true

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>; } } return false; } /** * Checks if the subtree under this node is the same as another subtree. * Returns null if it's equal, or a message describing the differences. */ public String checkTreeEquals(Node node2) { NodeMismatch diff = checkTreeEqualsImpl(node2); if (diff != null) { return "Node tree inequality:" + "\nTree1:\n" + toStringTree() + "\n\nTree2:\n" + node2.toStringTree() + "\n\nSubtree1: " + diff.nodeA.toStringTree() + "\n\nSubtree2: " + diff.nodeB.toStringTree(); } return null; } /** * If this is a compilation pass and not a test, do not construct error * strings. Instead return true if the trees are equal. */ public boolean checkTreeEqualsSilent(Node node2) { return checkTreeEqualsImpl(node2) == null; } /** * Helper function to ignore differences in Node subclasses that are no longer * used. */ @SuppressWarnings("unchecked") static private Class getNodeClass(Node n) { Class c = n.getClass(); if (c == FunctionNode.class || c == ScriptOrFnNode.class) { return Node.class; } return c; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getNodeClass(this) == getNodeClass(node2)) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTree

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>EqualsImpl(n2); if (res != null) { return res; } } return res; } /** * Checks if the subtree under this node is the same as another subtree * including types. Returns null if it's equal, or a message describing the * differences. */ public boolean checkTreeTypeAwareEqualsSilent(Node node2) { return checkTreeTypeAwareEqualsImpl(node2) == null; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getClass() == node2.getClass() && JSType.isEquivalent(jsType, node2.getJSType())) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } public static String tokenToName(int token) { switch (token) { case Token.ERROR: return "error"; case Token.EOF: return "eof"; case Token.EOL: return "eol"; case Token.ENTERWITH: return "enterwith"; case Token.LEAVEWITH: return "leavewith"; case Token.RETURN: return "return"; case Token.GOTO: return "goto"; case Token.IFEQ: return "ifeq"; case Token.IFNE: return "ifne"; case Token.SETNAME: return "setname"; case Token.BITOR: return "bitor"; case Token.BITXOR: return "bit

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>BREAK: return "break"; case Token.CONTINUE: return "continue"; case Token.VAR: return "var"; case Token.WITH: return "with"; case Token.CATCH: return "catch"; case Token.FINALLY: return "finally"; case Token.RESERVED: return "reserved"; case Token.NOT: return "not"; case Token.VOID: return "void"; case Token.BLOCK: return "block"; case Token.ARRAYLIT: return "arraylit"; case Token.OBJECTLIT: return "objectlit"; case Token.LABEL: return "label"; case Token.TARGET: return "target"; case Token.LOOP: return "loop"; case Token.EXPR_VOID: return "expr_void"; case Token.EXPR_RESULT: return "expr_result"; case Token.JSR: return "jsr"; case Token.SCRIPT: return "script"; case Token.EMPTY: return "empty"; case Token.GET_REF: return "get_ref"; case Token.REF_SPECIAL: return "ref_special"; } return "<unknown="+token+">"; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { if (type == Token.ARRAYLIT) { try { int[] indices1 = (int[]) getProp(Node.SKIP_INDEXES_PROP); int[] indices2 = (int[]) node.getProp(Node.SKIP_INDEXES_PROP); if (indices1 == null) { if (indices2 != null) { return false; } } else if (indices2 == null) { return false; } else if (indices1.length != indices2.length) { return false; } else { for (int i = 0; i < indices1.length; i++) { if (indices1[i] != indices2[i]) { return false; } } } } catch (Exception e) { return false; } } else if (type == Token.INC || type == Token.DEC) { int post1 = this.getIntProp(INCRDE

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>CR_PROP); int post2 = node.getIntProp(INCRDECR_PROP); if (post1 != post2) { return false; } } else if (type == Token.STRING) { int quoted1 = this.getIntProp(QUOTED_PROP); int quoted2 = node.getIntProp(QUOTED_PROP); if (quoted1 != quoted2) { return false; } } return true; } public boolean hasSideEffects() { switch (type) { case Token.EXPR_VOID: case Token.COMMA: if (last != null) return last.hasSideEffects(); else return true; case Token.HOOK: if (first == null || first.next == null || first.next.next == null) { Kit.codeBug(); } return first.next.hasSideEffects() && first.next.next.hasSideEffects(); case Token.ERROR: // Avoid cascaded error messages case Token.EXPR_RESULT: case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ENTERWITH: case Token.LEAVEWITH: case Token.RETURN: case Token.GOTO: case Token.IFEQ: case Token.IFNE: case Token.NEW: case Token.DELPROP: case Token.SETNAME: case Token.SETPROP: case Token.SETELEM: case Token.CALL: case Token.THROW: case Token.RETHROW: case Token.SETVAR: case Token.CATCH_SCOPE: case Token.RETURN_RESULT: case Token.SET_REF: case Token.DEL_REF: case Token.REF_CALL: case Token.TRY: case Token.SEMI: case Token.INC: case Token.DEC: case Token.EXPORT: case Token.IMPORT:

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null;) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "no next sibling."); Node child = prev.next; prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; return child; } /** * @return A detached clone of the Node, specifically excluding its children. */ public Node cloneNode() { Node result; try { result = (Node) super.clone(); // PropListItem lists are immutable and

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> can be shared so there is no // need to clone them here. result.next = null; result.first = null; result.last = null; result.parent = null; } catch (CloneNotSupportedException e) { throw new RuntimeException(e.getMessage()); } return result; } /** * @return A detached clone of the Node and all its children. */ public Node cloneTree() { Node result = cloneNode(); for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) { Node n2clone = n2.cloneTree(); n2clone.parent = result; if (result.last != null) { result.last.next = n2clone; } if (result.first == null) { result.first = n2clone; } result.last = n2clone; } return result; } /** * Copies source file and name information from the other * node given to the current node. Used for maintaining * debug information across node append and remove operations. * @return this */ public Node copyInformationFrom(Node other) { if (getProp(ORIGINALNAME_PROP) == null) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); } if (getProp(SOURCEFILE_PROP) == null) { putProp(SOURCEFILE_PROP, other.getProp(SOURCEFILE_PROP)); sourcePosition = other.sourcePosition; } return this; } /** * Copies source file and name information from the other node to the * entire tree rooted at this node. * @return this */ public Node copyInformationFromForTree(Node other) { copyInformationFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.copyInformationFromForTree(other); } return this; } //========================================================================== // Custom annotations public JSType getJSType() { return jsType; } public void setJSType(JSType jsType) { this.jsType = jsType; } public FileLevelJsDocBuilder getJsDocBuilder

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> body } Node init = forNode.getFirstChild(); Node cond = init.getNext(); Node iter = cond.getNext(); Node body = iter.getNext(); // After initialization, we transfer to the FOR which is in charge of // checking the condition (for the first time). createEdge(init, Branch.UNCOND, forNode); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode)); // The end of the body will have a unconditional branch to our iter // (handled by calling computeFollowNode of the last instruction of the // body. Our iter will jump to the forNode again to another condition // check. createEdge(iter, Branch.UNCOND, forNode); connectToPossibleExceptionHandler(init, init); connectToPossibleExceptionHandler(forNode, cond); connectToPossibleExceptionHandler(iter, iter); } else { // We have for (item in collection) { body } Node item = forNode.getFirstChild(); Node collection = item.getNext(); Node body = collection.getNext(); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode)); connectToPossibleExceptionHandler(forNode, collection); } } private void handleSwitch(Node node) { // Transfer to the first non-DEFAULT CASE. if there are none, transfer // to the DEFAULT or the EMPTY node. Node next = getNextSiblingOfType( node.getFirstChild().getNext(), Token.CASE, Token.EMPTY); if (next != null) { // Has at least one CASE or EMPTY createEdge(node, Branch.UNCOND, next); } else { // Has no CASE but possibly a DEFAULT if (node.getFirstChild().getNext() != null) { createEdge(node, Branch.UNCOND, node.getFirstChild().getNext()); } else { // No CASE, no DEFAULT createEdge(node

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>, Branch.UNCOND, computeFollowNode(node)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleCase(Node node) { // Case is a bit tricky....First it goes into the body if condition is true. createEdge(node, Branch.ON_TRUE, node.getFirstChild().getNext()); // Look for the next CASE, skipping over DEFAULT. Node next = getNextSiblingOfType(node.getNext(), Token.CASE); if (next != null) { // Found a CASE Preconditions.checkState(next.getType() == Token.CASE); createEdge(node, Branch.ON_FALSE, next); } else { // No more CASE found, go back and search for a DEFAULT. Node parent = node.getParent(); Node deflt = getNextSiblingOfType( parent.getFirstChild().getNext(), Token.DEFAULT); if (deflt != null) { // Has a DEFAULT createEdge(node, Branch.ON_FALSE, deflt); } else { // No DEFAULT found, go to the follow of the SWITCH. createEdge(node, Branch.ON_FALSE, computeFollowNode(node)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleDefault(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleWith(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getLastChild()); connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleStmtList(Node node) { Node parent = node.getParent(); // Special case, don't add a block of empty CATCH block to the graph. if (node.getType() == Token.BLOCK && parent != null && parent.getType() == Token.TRY && NodeUtil.getCatchBlock(parent) == node && !NodeUtil.hasCatchHandler(node)) { return; } // A block transfer control to its first child if it is not empty. Node child = node.getFirstChild(); // Function declarations are skipped since

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> control doesn't go into that // function (unless it is called) while (child != null && child.getType() == Token.FUNCTION) { child = child.getNext(); } if (child != null) { createEdge(node, Branch.UNCOND, computeFallThrough(child)); } else { createEdge(node, Branch.UNCOND, computeFollowNode(node)); } // Synthetic blocks if (parent != null) { switch (parent.getType()) { case Token.DEFAULT: case Token.CASE: case Token.TRY: break; default: if (node.getType() == Token.BLOCK && node.isSyntheticBlock()) { Node next = node.getLastChild(); if (next != null) { createEdge(node, Branch.SYN_BLOCK, computeFallThrough(next)); } } break; } } } private void handleFunction(Node node) { // A block transfer control to its first child if it is not empty. Preconditions.checkState(node.getChildCount() >= 3); createEdge(node, Branch.UNCOND, computeFallThrough(node.getFirstChild().getNext().getNext())); Preconditions.checkState(exceptionHandler.peek() == node); exceptionHandler.pop(); } private void handleExpr(Node node) { createEdge(node, Branch.UNCOND, computeFollowNode(node)); connectToPossibleExceptionHandler(node, node); } private void handleThrow(Node node) { connectToPossibleExceptionHandler(node, node); } private void handleTry(Node node) { createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleCatch(Node node) { createEdge(node, Branch.UNCOND, node.getLastChild()); } private void handleBreak(Node node) { String label = null; // See if it is a break with label. if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node lastJump; Node parent = node.getParent(); /* * Continuously look up the ancestor tree for the BREAK target or the target * with the corresponding label and connect to it. If along the path we *

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>(); } if (lastJump == node) { createEdge(node, Branch.UNCOND, iter); } else { finallyMap.put(lastJump, iter); } } private void handleReturn(Node node) { Node lastJump = null; for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext();) { Node curHandler = iter.next(); if (NodeUtil.isFunction(curHandler)) { break; } if (NodeUtil.hasFinally(curHandler)) { if (lastJump == null) { createEdge(node, Branch.UNCOND, curHandler.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(curHandler.getLastChild())); } lastJump = curHandler; } } if (node.hasChildren()) { connectToPossibleExceptionHandler(node, node.getFirstChild()); } if (lastJump == null) { createEdge(node, Branch.UNCOND, null); } else { finallyMap.put(lastJump, null); } } private void handleStmt(Node node) { // Simply transfer to the next line. createEdge(node, Branch.UNCOND, computeFollowNode(node)); connectToPossibleExceptionHandler(node, node); } private Node computeFollowNode(Node node) { return computeFollowNode(node, node); } /** * Computes the follow() node of a given node and its parent. There is a side * effect when calling this function. If this function computed an edge that * exists a FINALLY, it'll attempt to connect the fromNode to the outer * FINALLY according to the finallyMap. * * @param fromNode The original source node since {@code node} is changed * during recursion. * @param node The node that follow() should compute. */ private Node computeFollowNode(Node fromNode, Node node) { /* * This is the case where: * * 1. Parent is null implies that we are transferring control to the end of * the script. * * 2. Parent is a function implies that we are transferring control back to * the caller of the function. * *

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator<Var> vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { if (n.getType() == Token.FUNCTION || n.getType() == Token.SCRIPT) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild() || parent == scope.getRootNode(); } @Override public abstract void visit(NodeTraversal t, Node n, Node parent); /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ JSType getDeclaredTypeInAnnotation( NodeTraversal t, Node node, JSDocInfo info) { return getDeclaredTypeInAnnotation(t.getSourceName(), node, info); } JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.getType() == Token.GETPROP ? node.getFirstChild() : null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS>_VAR_DEF)); } for (Node name : n.children()) { defineName(name, n, parent, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, parent, (info != null) ? info : name.getJSDocInfo()); } break; case Token.FUNCTION: int parentType = parent.getType(); Preconditions.checkState( (scope.isLocal() || parentType != Token.ASSIGN) && parentType != Token.NAME, "function defined as standalone function when it is being " + "assigned"); String functionName = n.getFirstChild().getString(); FunctionType functionType = getFunctionType(functionName, n, info, null); if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } break; case Token.ASSIGN: // TODO(nicksantos): We should support direct assignment to a // prototype, as in: // Foo.prototype = { // a: function() { ... }, // b: function() { ... } // }; // Right now (6/23/08), we understand most of this syntax, but we // don't tie the "a" and "b" methods to the context of Foo. Node rvalue = n.getLastChild(); Node lvalue = n.getFirstChild(); info = (info != null) ? info : rvalue.getJSDocInfo(); if (rvalue.getType() == Token.FUNCTION || info != null && info.isConstructor()) { getFunctionType(lvalue.getQualifiedName(), rvalue, info, lvalue); } else if (info != null && info.hasEnumParameterType()) { JSType type = getEnumType(lvalue.getQualifiedName(), n, rvalue, info.getEnumParameterType().evaluate(scope, typeRegistry)); if (type != null) { setDeferredType(lvalue, type); } } break; default: throw new IllegalStateException(Integer.toString(n.getType())); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param

Closure, 147

<FILEB>
<CHANGES>
pType == Token.ASSIGN ||
pType == Token.OBJECTLIT)) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
for (Node next = block.getFirstChild();
next!= null && NodeUtil.isFunctionDeclaration(next);
next = next.getNext()) {
insertionPoint = next;
}
<CHANGEE>
<FILEE>
<FILEB> JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || <CHANGES> pType == Token.ASSIGN)) { <CHANGEE> return false; } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; <FILEE> <FILEB> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunction(n)) { visitFunction(t, n); } else if (n.getType() == Token.RETURN) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType funType = (FunctionType) n.getJSType(); Node block = n.getLastChild(); Node paramName = NodeUtil.getFnParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. <CHANGES> <CHANGEE> for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode(<SCANS> JSType elementsType) { EnumType enumType = null; // no value with @enum if (value != null) { if (value.getType() == Token.OBJECTLIT) { // collect enum elements enumType = typeRegistry.createEnumType(name, elementsType); // populate the enum type. Node key = value.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (enumType.hasOwnProperty(keyName)) { compiler.report(JSError.make(sourceName, key, ENUM_DUP, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName); } key = key.getNext(); key = (key == null) ? null : key.getNext(); } } else if (value.isQualifiedName()) { Var var = scope.getVar(value.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } } if (enumType == null) { compiler.report(JSError.make(sourceName, parent, ENUM_INITIALIZER)); } else if (scope.isGlobal()) { if (name != null && !name.isEmpty()) { typeRegistry.declareType(name, enumType.getElementsType()); } } return enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type